2 * mobile listview unit tests
5 // TODO split out into seperate test files
7 var home = $.mobile.path.parseUrl( location.href ).pathname + location.search,
8 insetVal = $.mobile.listview.prototype.options.inset;
10 $.mobile.defaultTransition = "none";
12 module( "Basic Linked list", {
14 if( location.hash != "#basic-linked-test" ){
17 $(document).one("pagechange", function() {
21 $.mobile.changePage( home );
25 teardown: function() {
26 $.mobile.listview.prototype.options.inset = insetVal;
30 asyncTest( "The page should enhanced correctly", function(){
31 setTimeout(function() {
32 ok($('#basic-linked-test .ui-li').length, ".ui-li classes added to li elements");
37 asyncTest( "Slides to the listview page when the li a is clicked", function() {
38 $.testHelper.pageSequence([
40 $.mobile.changePage("#basic-linked-test");
44 $('#basic-linked-test li a').first().click();
48 ok($('#basic-link-results').hasClass('ui-page-active'));
54 asyncTest( "Slides back to main page when back button is clicked", function() {
55 $.testHelper.pageSequence([
57 $.mobile.changePage("#basic-link-results");
61 window.history.back();
65 ok($('#basic-linked-test').hasClass('ui-page-active'));
71 asyncTest( "Presence of ui-li-has- classes", function(){
72 $.testHelper.pageSequence( [
74 $.mobile.changePage( "#ui-li-has-test" );
78 var page = $( ".ui-page-active" ),
79 items = page.find( "li" );
81 ok( items.eq( 0 ).hasClass( "ui-li-has-count"), "First LI should have ui-li-has-count class" );
82 ok( items.eq( 0 ).hasClass( "ui-li-has-arrow"), "First LI should have ui-li-has-arrow class" );
83 ok( !items.eq( 1 ).hasClass( "ui-li-has-count"), "Second LI should NOT have ui-li-has-count class" );
84 ok( items.eq( 1 ).hasClass( "ui-li-has-arrow"), "Second LI should have ui-li-has-arrow class" );
85 ok( !items.eq( 2 ).hasClass( "ui-li-has-count"), "Third LI should NOT have ui-li-has-count class" );
86 ok( !items.eq( 2 ).hasClass( "ui-li-has-arrow"), "Third LI should NOT have ui-li-has-arrow class" );
87 ok( items.eq( 3 ).hasClass( "ui-li-has-count"), "Fourth LI should have ui-li-has-count class" );
88 ok( !items.eq( 3 ).hasClass( "ui-li-has-arrow"), "Fourth LI should NOT have ui-li-has-arrow class" );
89 ok( !items.eq( 4 ).hasClass( "ui-li-has-count"), "Fifth LI should NOT have ui-li-has-count class" );
90 ok( !items.eq( 4 ).hasClass( "ui-li-has-arrow"), "Fifth LI should NOT have ui-li-has-arrow class" );
96 module('Nested List Test');
98 asyncTest( "Changes page to nested list test and enhances", function() {
99 $.testHelper.pageSequence([
101 $.mobile.changePage("#nested-list-test");
105 ok($('#nested-list-test').hasClass('ui-page-active'), "makes nested list test page active");
106 ok($(':jqmData(url="nested-list-test&ui-page=0-0")').length == 1, "Adds first UL to the page");
107 ok($(':jqmData(url="nested-list-test&ui-page=0-1")').length == 1, "Adds second nested UL to the page");
113 asyncTest( "change to nested page when the li a is clicked", function() {
115 $.testHelper.pageSequence([
117 $.mobile.changePage("#nested-list-test");
121 $('.ui-page-active li:eq(1) a:eq(0)').click();
125 var $new_page = $(':jqmData(url="nested-list-test&ui-page=0-0")');
127 ok($new_page.hasClass('ui-page-active'), 'Makes the nested page the active page.');
128 ok($('.ui-listview', $new_page).find(":contains('Rhumba of rattlesnakes')").length == 1, "The current page should have the proper text in the list.");
129 ok($('.ui-listview', $new_page).find(":contains('Shoal of Bass')").length == 1, "The current page should have the proper text in the list.");
135 asyncTest( "should go back to top level when the back button is clicked", function() {
136 $.testHelper.pageSequence([
138 $.mobile.changePage("#nested-list-test&ui-page=0-0");
142 window.history.back();
146 ok($('#nested-list-test').hasClass('ui-page-active'), 'Transitions back to the parent nested page');
152 test( "nested list title should use first text node, regardless of line breaks", function(){
153 // NOTE this is a super fragile reference to the nested page, any change to the list will break it
154 ok($(":jqmData(url='nested-list-test&ui-page=0-0') .ui-title").text() === "More animals", 'Text should be "More animals"');
157 asyncTest( "Multiple nested lists on a page with same labels", function() {
158 $.testHelper.pageSequence([
160 // https://github.com/jquery/jquery-mobile/issues/1617
161 $.mobile.changePage("#nested-lists-test");
165 // Click on the link of the third li element
166 $('.ui-page-active li:eq(2) a:eq(0)').click();
170 equal($('.ui-page-active .ui-content .ui-listview li').text(), "Item A-3-0Item A-3-1Item A-3-2", 'Text should be "Item A-3-0Item A-3-1Item A-3-2"');
176 module('Ordered Lists');
178 asyncTest( "changes to the numbered list page and enhances it", function() {
179 $.testHelper.pageSequence([
181 $.mobile.changePage("#numbered-list-test");
185 var $new_page = $('#numbered-list-test');
186 ok($new_page.hasClass('ui-page-active'), "Makes the new page active when the hash is changed.");
187 ok($('.ui-link-inherit', $new_page).first().text() == "Number 1", "The text of the first LI should be Number 1");
193 asyncTest( "changes to number 1 page when the li a is clicked", function() {
194 $.testHelper.pageSequence([
196 $('#numbered-list-test li a').first().click();
200 ok($('#numbered-list-results').hasClass('ui-page-active'), "The new numbered page was transitioned correctly.");
206 asyncTest( "takes us back to the numbered list when the back button is clicked", function() {
207 $.testHelper.pageSequence([
209 $.mobile.changePage('#numbered-list-test');
213 $.mobile.changePage('#numbered-list-results');
217 window.history.back();
221 ok($('#numbered-list-test').hasClass('ui-page-active'));
227 module('Read only list');
229 asyncTest( "changes to the read only page when hash is changed", function() {
230 $.testHelper.pageSequence([
232 $.mobile.changePage("#read-only-list-test");
236 var $new_page = $('#read-only-list-test');
237 ok($new_page.hasClass('ui-page-active'), "makes the read only page the active page");
238 ok($('li', $new_page).first().text() === "Read", "The first LI has the proper text.");
244 module('Split view list');
246 asyncTest( "changes the page to the split view list and enhances it correctly.", function() {
247 $.testHelper.pageSequence([
249 $.mobile.changePage("#split-list-test");
253 var $new_page = $('#split-list-test');
254 ok($('.ui-li-link-alt', $new_page).length == 3);
255 ok($('.ui-link-inherit', $new_page).length == 3);
261 asyncTest( "change the page to the split view page 1 when the first link is clicked", function() {
262 $.testHelper.pageSequence([
264 $.mobile.changePage("#split-list-test");
268 $('.ui-page-active .ui-li a:eq(0)').click();
272 ok($('#split-list-link1').hasClass('ui-page-active'));
278 asyncTest( "Slide back to the parent list view when the back button is clicked", function() {
279 $.testHelper.pageSequence([
281 $.mobile.changePage("#split-list-test");
285 $('.ui-page-active .ui-listview a:eq(0)').click();
293 ok($('#split-list-test').hasClass('ui-page-active'));
299 asyncTest( "Clicking on the icon (the second link) should take the user to other a href of this LI", function() {
300 $.testHelper.pageSequence([
302 $.mobile.changePage("#split-list-test");
306 $('.ui-page-active .ui-li-link-alt:eq(0)').click();
310 ok($('#split-list-link2').hasClass('ui-page-active'));
316 module( "List Dividers" );
318 asyncTest( "Makes the list divider page the active page and enhances it correctly.", function() {
319 $.testHelper.pageSequence([
321 $.mobile.changePage("#list-divider-test");
325 var $new_page = $('#list-divider-test');
326 ok($new_page.find('.ui-li-divider').length == 2);
327 ok($new_page.hasClass('ui-page-active'));
333 module( "Search Filter");
335 var searchFilterId = "#search-filter-test";
338 asyncTest( "Filter downs results when the user enters information", function() {
339 var $searchPage = $(searchFilterId);
340 $.testHelper.pageSequence([
342 $.mobile.changePage(searchFilterId);
346 $searchPage.find('input').val('at');
347 $searchPage.find('input').trigger('change');
349 same($searchPage.find('li.ui-screen-hidden').length, 2);
355 asyncTest( "Redisplay results when user removes values", function() {
356 var $searchPage = $(searchFilterId);
357 $.testHelper.pageSequence([
359 $.mobile.changePage(searchFilterId);
363 $searchPage.find('input').val('a');
364 $searchPage.find('input').trigger('change');
366 same($searchPage.find("li[style^='display: none;']").length, 0);
372 asyncTest( "Filter works fine with \\W- or regexp-special-characters", function() {
373 var $searchPage = $(searchFilterId);
374 $.testHelper.pageSequence([
376 $.mobile.changePage(searchFilterId);
380 $searchPage.find('input').val('*');
381 $searchPage.find('input').trigger('change');
383 same($searchPage.find('li.ui-screen-hidden').length, 4);
389 test( "Refresh applies thumb styling", function(){
390 var ul = $('.ui-page-active ul');
392 ul.append("<li id='fiz'><img/></li>");
393 ok(!ul.find("#fiz img").hasClass("ui-li-thumb"));
394 ul.listview('refresh');
395 ok(ul.find("#fiz img").hasClass("ui-li-thumb"));
398 asyncTest( "Filter downs results and dividers when the user enters information", function() {
399 var $searchPage = $("#search-filter-with-dividers-test");
400 $.testHelper.pageSequence([
402 $.mobile.changePage("#search-filter-with-dividers-test");
405 // wait for the page to become active/enhanced
407 $searchPage.find('input').val('at');
408 $searchPage.find('input').trigger('change');
409 setTimeout(function() {
410 //there should be four hidden list entries
411 same($searchPage.find('li.ui-screen-hidden').length, 4);
413 //there should be two list entries that are list dividers and hidden
414 same($searchPage.find('li.ui-screen-hidden:jqmData(role=list-divider)').length, 2);
416 //there should be two list entries that are not list dividers and hidden
417 same($searchPage.find('li.ui-screen-hidden:not(:jqmData(role=list-divider))').length, 2);
424 asyncTest( "Redisplay results when user removes values", function() {
425 $.testHelper.pageSequence([
427 $.mobile.changePage("#search-filter-with-dividers-test");
431 $('.ui-page-active input').val('a');
432 $('.ui-page-active input').trigger('change');
434 setTimeout(function() {
435 same($('.ui-page-active input').val(), 'a');
436 same($('.ui-page-active li[style^="display: none;"]').length, 0);
443 asyncTest( "Dividers are hidden when preceding hidden rows and shown when preceding shown rows", function () {
444 $.testHelper.pageSequence([
446 $.mobile.changePage("#search-filter-with-dividers-test");
450 var $page = $('.ui-page-active');
452 $page.find('input').val('at');
453 $page.find('input').trigger('change');
455 setTimeout(function() {
456 same($page.find('li:jqmData(role=list-divider):hidden').length, 2);
457 same($page.find('li:jqmData(role=list-divider):hidden + li:not(:jqmData(role=list-divider)):hidden').length, 2);
458 same($page.find('li:jqmData(role=list-divider):not(:hidden) + li:not(:jqmData(role=list-divider)):not([:hidden)').length, 2);
465 asyncTest( "Inset List View should refresh corner classes after filtering", 4 * 2, function () {
466 var checkClasses = function() {
467 var $page = $( ".ui-page-active" ),
468 $li = $page.find( "li:visible" );
469 ok($li.first().hasClass( "ui-corner-top" ), $li.length+" li elements: First visible element should have class ui-corner-top");
470 ok($li.last().hasClass( "ui-corner-bottom" ), $li.length+" li elements: Last visible element should have class ui-corner-bottom");
473 $.testHelper.pageSequence([
475 $.mobile.changePage("#search-filter-inset-test");
479 var $page = $('.ui-page-active');
480 $.testHelper.sequence([
484 $page.find('input').val('man');
485 $page.find('input').trigger('change');
491 $page.find('input').val('at');
492 $page.find('input').trigger('change');
498 $page.find('input').val('catwoman');
499 $page.find('input').trigger('change');
511 module( "Programmatically generated list items", {
533 $( "#programmatically-generated-list-items" ).html("");
535 for ( var i = 0, len = data.length; i < len; i++ ) {
536 item = $( '<li id="myItem' + data[i].id + '">' );
537 label = $( "<strong>" + data[i].label + "</strong>").appendTo( item );
538 $( "#programmatically-generated-list-items" ).append( item );
543 asyncTest( "Corner styling on programmatically created list items", function() {
544 // https://github.com/jquery/jquery-mobile/issues/1470
545 $.testHelper.pageSequence([
547 $.mobile.changePage( "#programmatically-generated-list" );
550 ok(!$( "#programmatically-generated-list-items li:first-child" ).hasClass( "ui-corner-bottom" ), "First list item should not have class ui-corner-bottom" );
556 module("Programmatic list items manipulation");
558 asyncTest("Removing list items", 4, function() {
559 $.testHelper.pageSequence([
561 $.mobile.changePage("#removing-items-from-list-test");
565 var ul = $('#removing-items-from-list-test ul');
566 ul.find("li").first().remove();
567 equal(ul.find("li").length, 3, "There should be only 3 list items left");
569 ul.listview('refresh');
570 ok(ul.find("li").first().hasClass("ui-corner-top"), "First list item should have class ui-corner-top");
572 ul.find("li").last().remove();
573 equal(ul.find("li").length, 2, "There should be only 2 list items left");
575 ul.listview('refresh');
576 ok(ul.find("li").last().hasClass("ui-corner-bottom"), "Last list item should have class ui-corner-bottom");
582 module("Rounded corners");
584 asyncTest("Top and bottom corners rounded in inset list", 14, function() {
585 $.testHelper.pageSequence([
587 $.mobile.changePage("#corner-rounded-test");
591 var ul = $('#corner-rounded-test ul');
593 for( var t = 0; t<3; t++){
594 ul.append("<li>Item " + t + "</li>");
595 ul.listview('refresh');
596 equals(ul.find(".ui-corner-top").length, 1, "There should be only one element with class ui-corner-top");
597 equals(ul.find("li:visible").first()[0], ul.find(".ui-corner-top")[0], "First list item should have class ui-corner-top in list with " + ul.find("li").length + " item(s)");
598 equals(ul.find(".ui-corner-bottom").length, 1, "There should be only one element with class ui-corner-bottom");
599 equals(ul.find("li:visible").last()[0], ul.find(".ui-corner-bottom")[0], "Last list item should have class ui-corner-bottom in list with " + ul.find("li").length + " item(s)");
602 ul.find( "li" ).first().hide();
603 ul.listview( "refresh" );
604 equals(ul.find("li:visible").first()[0], ul.find(".ui-corner-top")[0], "First visible list item should have class ui-corner-top");
606 ul.find( "li" ).last().hide();
607 ul.listview( "refresh" );
608 equals(ul.find("li:visible").last()[0], ul.find(".ui-corner-bottom")[0], "Last visible list item should have class ui-corner-bottom");
615 test( "Listview will create when inside a container that receives a 'create' event", function(){
616 ok( !$("#enhancetest").appendTo(".ui-page-active").find(".ui-listview").length, "did not have enhancements applied" );
617 ok( $("#enhancetest").trigger("create").find(".ui-listview").length, "enhancements applied" );
620 module( "Cached Linked List" );
622 var findNestedPages = function(selector){
623 return $( selector + " #topmost" ).listview( 'childPages' );
626 asyncTest( "nested pages are removed from the dom by default", function(){
627 $.testHelper.pageSequence([
629 //reset for relative url refs
630 $.mobile.changePage( home );
634 $.mobile.changePage( "cache-tests/uncached-nested.html" );
638 ok( findNestedPages( "#uncached-nested-list" ).length > 0, "verify that there are nested pages" );
639 $.mobile.changePage( home );
643 $.mobile.changePage( "cache-tests/clear.html" );
647 same( findNestedPages( "#uncached-nested-list" ).length, 0 );
653 asyncTest( "nested pages preserved when parent page is cached", function(){
655 $.testHelper.pageSequence([
657 //reset for relative url refs
658 $.mobile.changePage( home );
662 $.mobile.changePage( "cache-tests/cached-nested.html" );
666 ok( findNestedPages( "#cached-nested-list" ).length > 0, "verify that there are nested pages" );
667 $.mobile.changePage( home );
671 $.mobile.changePage( "cache-tests/clear.html" );
675 ok( findNestedPages( "#cached-nested-list" ).length > 0, "nested pages remain" );
681 asyncTest( "parent page is not removed when visiting a sub page", function(){
682 $.testHelper.pageSequence([
684 //reset for relative url refs
685 $.mobile.changePage( home );
689 $.mobile.changePage( "cache-tests/cached-nested.html" );
693 same( $("#cached-nested-list").length, 1 );
694 $.mobile.changePage( home );
698 $.mobile.changePage( "cache-tests/clear.html" );
702 same( $("#cached-nested-list").length, 1 );
708 asyncTest( "filterCallback can be altered after widget creation", function(){
709 var listPage = $( "#search-filter-test" );
710 expect( listPage.find("li").length );
712 $.testHelper.pageSequence( [
714 //reset for relative url refs
715 $.mobile.changePage( home );
719 $.mobile.changePage( "#search-filter-test" );
723 // set the listview instance callback
724 listPage.find( "ul" ).listview( "option", "filterCallback", function() {
725 ok(true, "custom callback invoked");
728 // trigger a change in the search filter
729 listPage.find( "input" ).val( "foo" ).trigger( "change" );
731 //NOTE beware a poossible issue with timing here
737 asyncTest( "nested pages hash key is always in the hash (replaceState)", function(){
738 $.testHelper.pageSequence([
740 //reset for relative url refs
741 $.mobile.changePage( home );
745 // https://github.com/jquery/jquery-mobile/issues/1617
746 $.mobile.changePage("#nested-lists-test");
750 // Click on the link of the third li element
751 $('.ui-page-active li:eq(2) a:eq(0)').click();
755 ok( location.hash.search($.mobile.subPageUrlKey) >= 0 );
761 asyncTest( "embedded listview page with nested pages is not removed from the dom", function() {
762 $.testHelper.pageSequence([
764 // open the nested list page
765 same( $("div#nested-list-test").length, 1 );
766 $( "a#nested-list-test-anchor" ).click();
770 // go back to the origin page
771 window.history.back();
775 // make sure the page is still in place
776 same( $("div#nested-list-test").length, 1 );
783 asyncTest( "list inherits theme from parent", function() {
784 $.testHelper.pageSequence([
786 $.mobile.changePage("#list-theme-inherit");
790 var theme = $.mobile.activePage.jqmData('theme');
791 ok( $.mobile.activePage.find("ul > li").hasClass("ui-body-b"), "theme matches the parent");
792 window.history.back();
799 asyncTest( "list filter is inset from prototype options value", function() {
800 $.mobile.listview.prototype.options.inset = true;
801 $("#list-inset-filter-prototype").page();
803 $.testHelper.pageSequence([
805 $.mobile.changePage("#list-inset-filter-prototype");
808 function( timedOut) {
810 same( $.mobile.activePage.find("form.ui-listview-filter-inset").length, 1, "form is inset");
811 window.history.back();
818 asyncTest( "list filter is inset from data attr value", function() {
819 $.mobile.listview.prototype.options.inset = false;
820 $("#list-inset-filter-data-attr").page();
822 $.testHelper.pageSequence([
824 $.mobile.changePage("#list-inset-filter-data-attr");
827 function( timedOut) {
829 same( $.mobile.activePage.find("form.ui-listview-filter-inset").length, 1, "form is inset");
830 window.history.back();
837 asyncTest( "split list items respect the icon", function() {
838 $.testHelper.pageSequence([
840 $.mobile.changePage("#split-list-icon");
844 $.mobile.activePage.find("li").each(function(i, elem){
846 order = [ "star", "plug", "delete", "plug" ];
848 same( $elem.find("span.ui-icon-" + order[i]).length, 1, "there should be one " + order[i] + " icon" );
851 window.history.back();