diff options
Diffstat (limited to 'apps/it/static/js/ui/tests/unit')
154 files changed, 21170 insertions, 0 deletions
diff --git a/apps/it/static/js/ui/tests/unit/accordion/accordion.html b/apps/it/static/js/ui/tests/unit/accordion/accordion.html new file mode 100644 index 0000000..67928fd --- /dev/null +++ b/apps/it/static/js/ui/tests/unit/accordion/accordion.html @@ -0,0 +1,143 @@ +<!doctype html> +<html lang="en"> +<head> + <meta charset="utf-8"> + <title>jQuery UI Accordion Test Suite</title> + + <script src="../../jquery.js"></script> + <link rel="stylesheet" href="../../../external/qunit.css"> + <script src="../../../external/qunit.js"></script> + <script src="../../jquery.simulate.js"></script> + <script src="../testsuite.js"></script> + <script> + TestHelpers.loadResources({ + css: [ "ui.core", "ui.accordion" ], + js: [ + "ui/jquery.ui.core.js", + "ui/jquery.ui.widget.js", + "ui/jquery.ui.accordion.js" + ] + }); + </script> + + <script src="accordion_test_helpers.js"></script> + <script src="accordion_common.js"></script> + <script src="accordion_core.js"></script> + <script src="accordion_events.js"></script> + <script src="accordion_methods.js"></script> + <script src="accordion_options.js"></script> + + <script src="../swarminject.js"></script> + <style> + #list, #list1 *, #navigation, #navigation * { + margin: 0; + padding: 0; + font-size: 12px; + line-height: 15px; + } + /* avoid IE7 oscillating between overflow visible and scroll values */ + #list1 > div { + overflow: visible; + } + </style> +</head> +<body> +<h1 id="qunit-header">jQuery UI Accordion Test Suite</h1> +<h2 id="qunit-banner"></h2> +<div id="qunit-testrunner-toolbar"></div> +<h2 id="qunit-userAgent"></h2> +<ol id="qunit-tests"></ol> +<div id="qunit-fixture"> + +<div id="list1" class="foo"> + <h3 class="bar">There is one obvious advantage:</h3> + <div class="foo"> + <p> + You've seen it coming! + <br> + Buy now and get nothing for free! + <br> + Well, at least no free beer. Perhaps a bear, if you can afford it. + </p> + </div> + <h3 class="bar">Now that you've got...</h3> + <div class="foo"> + <p> + your bear, you have to admit it! + <br> + No, we aren't <a href="#">selling bears</a>. + </p> + <p> + We could talk about renting one. + </p> + </div> + <h3 class="bar">Rent one bear, ...</h3> + <div class="foo"> + <p> + get two for three beer. + </p> + <p> + And now, for something completely different. + </p> + </div> +</div> + +<div id="navigationWrapper"> + <ul id="navigation"> + <li> + <h2><a href="?p=1.1.1">Guitar</a></h2> + <ul> + <li><a href="?p=1.1.1.1">Electric</a></li> + <li><a href="?p=1.1.1.2">Acoustic</a></li> + <li><a href="?p=1.1.1.3">Amps</a></li> + <li><a href="?p=1.1.1.4">Effects</a></li> + <li><a href="?p=1.1.1.5">Accessories</a></li> + </ul> + </li> + <li> + <h2><a href="?p=1.1.2"><span>Bass</span></a></h2> + <ul> + <li><a href="?p=1.1.2.1">Electric</a></li> + <li><a href="?p=1.1.2.2">Acoustic</a></li> + <li><a href="?p=1.1.2.3">Amps</a></li> + <li><a href="?p=1.1.2.4">Effects</a></li> + <li><a href="?p=1.1.2.5">Accessories</a></li> + <li><a href="?p=1.1.2.5">Accessories</a></li> + <li><a href="?p=1.1.2.5">Accessories</a></li> + </ul> + </li> + <li> + <h2><a href="?p=1.1.3">Drums</a></h2> + <ul> + <li><a href="?p=1.1.3.2">Acoustic</a></li> + <li><a href="?p=1.1.3.3">Electronic</a></li> + <li><a href="?p=1.1.3.6">Accessories</a></li> + </ul> + </li> + </ul> +</div> + +<dl id="accordion-dl"> + <dt> + Accordion Header 1 + </dt> + <dd> + Accordion Content 1 + </dd> + <dt> + Accordion Header 2 + </dt> + <dd> + Accordion Content 2 + </dd> + <dt> + Accordion Header 3 + </dt> + <dd> + Accordion Content 3 + </dd> +</dl> + +</div> +</body> +</html> diff --git a/apps/it/static/js/ui/tests/unit/accordion/accordion_common.js b/apps/it/static/js/ui/tests/unit/accordion/accordion_common.js new file mode 100644 index 0000000..ef24cf2 --- /dev/null +++ b/apps/it/static/js/ui/tests/unit/accordion/accordion_common.js @@ -0,0 +1,20 @@ +TestHelpers.commonWidgetTests( "accordion", { + defaults: { + active: 0, + animate: {}, + collapsible: false, + disabled: false, + event: "click", + header: "> li > :first-child,> :not(li):even", + heightStyle: "auto", + icons: { + "activeHeader": "ui-icon-triangle-1-s", + "header": "ui-icon-triangle-1-e" + }, + + // callbacks + activate: null, + beforeActivate: null, + create: null + } +}); diff --git a/apps/it/static/js/ui/tests/unit/accordion/accordion_core.js b/apps/it/static/js/ui/tests/unit/accordion/accordion_core.js new file mode 100644 index 0000000..8e0175a --- /dev/null +++ b/apps/it/static/js/ui/tests/unit/accordion/accordion_core.js @@ -0,0 +1,117 @@ +(function( $ ) { + +var setupTeardown = TestHelpers.accordion.setupTeardown, + state = TestHelpers.accordion.state; + +module( "accordion: core", setupTeardown() ); + +$.each( { div: "#list1", ul: "#navigation", dl: "#accordion-dl" }, function( type, selector ) { + test( "markup structure: " + type, function() { + expect( 4 ); + var element = $( selector ).accordion(); + ok( element.hasClass( "ui-accordion" ), "main element is .ui-accordion" ); + equal( element.find( ".ui-accordion-header" ).length, 3, + ".ui-accordion-header elements exist, correct number" ); + equal( element.find( ".ui-accordion-content" ).length, 3, + ".ui-accordion-content elements exist, correct number" ); + deepEqual( element.find( ".ui-accordion-header" ).next().get(), + element.find( ".ui-accordion-content" ).get(), + "content panels come immediately after headers" ); + }); +}); + +test( "handle click on header-descendant", function() { + expect( 1 ); + var element = $( "#navigation" ).accordion(); + $( "#navigation h2:eq(1) a" ).click(); + state( element, 0, 1, 0 ); +}); + +test( "accessibility", function () { + expect( 37 ); + var element = $( "#list1" ).accordion({ + active: 1 + }), + headers = element.find( ".ui-accordion-header" ); + + equal( element.attr( "role" ), "tablist", "element role" ); + headers.each(function( i ) { + var header = headers.eq( i ), + panel = header.next(); + equal( header.attr( "role" ), "tab", "header " + i + " role" ); + equal( header.attr( "aria-controls" ), panel.attr( "id" ), "header " + i + " aria-controls" ); + equal( panel.attr( "role" ), "tabpanel", "panel " + i + " role" ); + equal( panel.attr( "aria-labelledby" ), header.attr( "id" ), "panel " + i + " aria-labelledby" ); + }); + + equal( headers.eq( 1 ).attr( "tabindex" ), 0, "active header has tabindex=0" ); + equal( headers.eq( 1 ).attr( "aria-selected" ), "true", "active tab has aria-selected=true" ); + equal( headers.eq( 1 ).attr( "aria-expanded" ), "true", "active tab has aria-expanded=true" ); + equal( headers.eq( 1 ).next().attr( "aria-hidden" ), "false", "active tabpanel has aria-hidden=false" ); + equal( headers.eq( 0 ).attr( "tabindex" ), -1, "inactive header has tabindex=-1" ); + equal( headers.eq( 0 ).attr( "aria-selected" ), "false", "inactive tab has aria-selected=false" ); + equal( headers.eq( 0 ).attr( "aria-expanded" ), "false", "inactive tab has aria-expanded=false" ); + equal( headers.eq( 0 ).next().attr( "aria-hidden" ), "true", "inactive tabpanel has aria-hidden=true" ); + equal( headers.eq( 2 ).attr( "tabindex" ), -1, "inactive header has tabindex=-1" ); + equal( headers.eq( 2 ).attr( "aria-selected" ), "false", "inactive tab has aria-selected=false" ); + equal( headers.eq( 2 ).attr( "aria-expanded" ), "false", "inactive tab has aria-expanded=false" ); + equal( headers.eq( 2 ).next().attr( "aria-hidden" ), "true", "inactive tabpanel has aria-hidden=true" ); + + element.accordion( "option", "active", 0 ); + equal( headers.eq( 0 ).attr( "tabindex" ), 0, "active header has tabindex=0" ); + equal( headers.eq( 0 ).attr( "aria-selected" ), "true", "active tab has aria-selected=true" ); + equal( headers.eq( 0 ).attr( "aria-expanded" ), "true", "active tab has aria-expanded=true" ); + equal( headers.eq( 0 ).next().attr( "aria-hidden" ), "false", "active tabpanel has aria-hidden=false" ); + equal( headers.eq( 1 ).attr( "tabindex" ), -1, "inactive header has tabindex=-1" ); + equal( headers.eq( 1 ).attr( "aria-selected" ), "false", "inactive tab has aria-selected=false" ); + equal( headers.eq( 1 ).attr( "aria-expanded" ), "false", "inactive tab has aria-expanded=false" ); + equal( headers.eq( 1 ).next().attr( "aria-hidden" ), "true", "inactive tabpanel has aria-hidden=true" ); + equal( headers.eq( 2 ).attr( "tabindex" ), -1, "inactive header has tabindex=-1" ); + equal( headers.eq( 2 ).attr( "aria-selected" ), "false", "inactive tab has aria-selected=false" ); + equal( headers.eq( 2 ).attr( "aria-expanded" ), "false", "inactive tab has aria-expanded=false" ); + equal( headers.eq( 2 ).next().attr( "aria-hidden" ), "true", "inactive tabpanel has aria-hidden=true" ); +}); + +asyncTest( "keyboard support", function() { + expect( 13 ); + var element = $( "#list1" ).accordion(), + headers = element.find( ".ui-accordion-header" ), + anchor = headers.eq( 1 ).next().find( "a" ).eq( 0 ), + keyCode = $.ui.keyCode; + equal( headers.filter( ".ui-state-focus" ).length, 0, "no headers focused on init" ); + headers.eq( 0 ).simulate( "focus" ); + setTimeout(function() { + ok( headers.eq( 0 ).is( ".ui-state-focus" ), "first header has focus" ); + headers.eq( 0 ).simulate( "keydown", { keyCode: keyCode.DOWN } ); + ok( headers.eq( 1 ).is( ".ui-state-focus" ), "DOWN moves focus to next header" ); + headers.eq( 1 ).simulate( "keydown", { keyCode: keyCode.RIGHT } ); + ok( headers.eq( 2 ).is( ".ui-state-focus" ), "RIGHT moves focus to next header" ); + headers.eq( 2 ).simulate( "keydown", { keyCode: keyCode.DOWN } ); + ok( headers.eq( 0 ).is( ".ui-state-focus" ), "DOWN wraps focus to first header" ); + + headers.eq( 0 ).simulate( "keydown", { keyCode: keyCode.UP } ); + ok( headers.eq( 2 ).is( ".ui-state-focus" ), "UP wraps focus to last header" ); + headers.eq( 2 ).simulate( "keydown", { keyCode: keyCode.LEFT } ); + ok( headers.eq( 1 ).is( ".ui-state-focus" ), "LEFT moves focus to previous header" ); + + headers.eq( 1 ).simulate( "keydown", { keyCode: keyCode.HOME } ); + ok( headers.eq( 0 ).is( ".ui-state-focus" ), "HOME moves focus to first header" ); + headers.eq( 0 ).simulate( "keydown", { keyCode: keyCode.END } ); + ok( headers.eq( 2 ).is( ".ui-state-focus" ), "END moves focus to last header" ); + + headers.eq( 2 ).simulate( "keydown", { keyCode: keyCode.ENTER } ); + equal( element.accordion( "option", "active" ) , 2, "ENTER activates panel" ); + headers.eq( 1 ).simulate( "keydown", { keyCode: keyCode.SPACE } ); + equal( element.accordion( "option", "active" ), 1, "SPACE activates panel" ); + + anchor.simulate( "focus" ); + setTimeout(function() { + ok( !headers.eq( 1 ).is( ".ui-state-focus" ), "header loses focus when focusing inside the panel" ); + anchor.simulate( "keydown", { keyCode: keyCode.UP, ctrlKey: true } ); + ok( headers.eq( 1 ).is( ".ui-state-focus" ), "CTRL+UP moves focus to header" ); + start(); + }, 1 ); + }, 1 ); +}); + +}( jQuery ) ); diff --git a/apps/it/static/js/ui/tests/unit/accordion/accordion_events.js b/apps/it/static/js/ui/tests/unit/accordion/accordion_events.js new file mode 100644 index 0000000..79283d1 --- /dev/null +++ b/apps/it/static/js/ui/tests/unit/accordion/accordion_events.js @@ -0,0 +1,169 @@ +(function( $ ) { + +var setupTeardown = TestHelpers.accordion.setupTeardown, + state = TestHelpers.accordion.state; + +module( "accordion: events", setupTeardown() ); + +test( "create", function() { + expect( 15 ); + + var element = $( "#list1" ), + headers = element.children( "h3" ), + contents = headers.next(); + + element.accordion({ + create: function( event, ui ) { + equal( ui.header.length, 1, "header length" ); + strictEqual( ui.header[ 0 ], headers[ 0 ], "header" ); + equal( ui.panel.length, 1, "panel length" ); + strictEqual( ui.panel[ 0 ], contents[ 0 ], "panel" ); + equal( ui.content.length, 1, "content length" ); + strictEqual( ui.content[ 0 ], contents[ 0 ], "content" ); + } + }); + element.accordion( "destroy" ); + + element.accordion({ + active: 2, + create: function( event, ui ) { + equal( ui.header.length, 1, "header length" ); + strictEqual( ui.header[ 0 ], headers[ 2 ], "header" ); + equal( ui.panel.length, 1, "panel length" ); + strictEqual( ui.panel[ 0 ], contents[ 2 ], "panel" ); + equal( ui.content.length, 1, "content length" ); + strictEqual( ui.content[ 0 ], contents[ 2 ], "content" ); + } + }); + element.accordion( "destroy" ); + + element.accordion({ + active: false, + collapsible: true, + create: function( event, ui ) { + equal( ui.header.length, 0, "header length" ); + equal( ui.panel.length, 0, "panel length" ); + equal( ui.content.length, 0, "content length" ); + } + }); + element.accordion( "destroy" ); +}); + +test( "beforeActivate", function() { + expect( 38 ); + var element = $( "#list1" ).accordion({ + active: false, + collapsible: true + }), + headers = element.find( ".ui-accordion-header" ), + content = element.find( ".ui-accordion-content" ); + + element.one( "accordionbeforeactivate", function( event, ui ) { + ok( !( "originalEvent" in event ) ); + equal( ui.oldHeader.length, 0 ); + equal( ui.oldPanel.length, 0 ); + equal( ui.newHeader.length, 1 ); + strictEqual( ui.newHeader[ 0 ], headers[ 0 ] ); + equal( ui.newPanel.length, 1 ); + strictEqual( ui.newPanel[ 0 ], content[ 0 ] ); + state( element, 0, 0, 0 ); + }); + element.accordion( "option", "active", 0 ); + state( element, 1, 0, 0 ); + + element.one( "accordionbeforeactivate", function( event, ui ) { + equal( event.originalEvent.type, "click" ); + equal( ui.oldHeader.length, 1 ); + strictEqual( ui.oldHeader[ 0 ], headers[ 0 ] ); + equal( ui.oldPanel.length, 1 ); + strictEqual( ui.oldPanel[ 0 ], content[ 0 ] ); + equal( ui.newHeader.length, 1 ); + strictEqual( ui.newHeader[ 0 ], headers[ 1 ] ); + equal( ui.newPanel.length, 1 ); + strictEqual( ui.newPanel[ 0 ], content[ 1 ] ); + state( element, 1, 0, 0 ); + }); + headers.eq( 1 ).click(); + state( element, 0, 1, 0 ); + + element.one( "accordionbeforeactivate", function( event, ui ) { + ok( !( "originalEvent" in event ) ); + equal( ui.oldHeader.length, 1 ); + strictEqual( ui.oldHeader[ 0 ], headers[ 1 ] ); + equal( ui.oldPanel.length, 1 ); + strictEqual( ui.oldPanel[ 0 ], content[ 1 ] ); + equal( ui.newHeader.length, 0 ); + equal( ui.newPanel.length, 0 ); + state( element, 0, 1, 0 ); + }); + element.accordion( "option", "active", false ); + state( element, 0, 0, 0 ); + + element.one( "accordionbeforeactivate", function( event, ui ) { + ok( !( "originalEvent" in event ) ); + equal( ui.oldHeader.length, 0 ); + equal( ui.oldPanel.length, 0 ); + equal( ui.newHeader.length, 1 ); + strictEqual( ui.newHeader[ 0 ], headers[ 2 ] ); + equal( ui.newPanel.length, 1 ); + strictEqual( ui.newPanel[ 0 ], content[ 2 ] ); + event.preventDefault(); + state( element, 0, 0, 0 ); + }); + element.accordion( "option", "active", 2 ); + state( element, 0, 0, 0 ); +}); + +test( "activate", function() { + expect( 21 ); + var element = $( "#list1" ).accordion({ + active: false, + collapsible: true + }), + headers = element.find( ".ui-accordion-header" ), + content = element.find( ".ui-accordion-content" ); + + element.one( "accordionactivate", function( event, ui ) { + equal( ui.oldHeader.length, 0 ); + equal( ui.oldPanel.length, 0 ); + equal( ui.newHeader.length, 1 ); + strictEqual( ui.newHeader[ 0 ], headers[ 0 ] ); + equal( ui.newPanel.length, 1 ); + strictEqual( ui.newPanel[ 0 ], content[ 0 ] ); + }); + element.accordion( "option", "active", 0 ); + + element.one( "accordionactivate", function( event, ui ) { + equal( ui.oldHeader.length, 1 ); + strictEqual( ui.oldHeader[ 0 ], headers[ 0 ] ); + equal( ui.oldPanel.length, 1 ); + strictEqual( ui.oldPanel[ 0 ], content[ 0 ] ); + equal( ui.newHeader.length, 1 ); + strictEqual( ui.newHeader[ 0 ], headers[ 1 ] ); + equal( ui.newPanel.length, 1 ); + strictEqual( ui.newPanel[ 0 ], content[ 1 ] ); + }); + headers.eq( 1 ).click(); + + element.one( "accordionactivate", function( event, ui ) { + equal( ui.oldHeader.length, 1 ); + strictEqual( ui.oldHeader[ 0 ], headers[ 1 ] ); + equal( ui.oldPanel.length, 1 ); + strictEqual( ui.oldPanel[ 0 ], content[ 1 ] ); + equal( ui.newHeader.length, 0 ); + equal( ui.newPanel.length, 0 ); + }); + element.accordion( "option", "active", false ); + + // prevent activation + element.one( "accordionbeforeactivate", function( event ) { + ok( true ); + event.preventDefault(); + }); + element.one( "accordionactivate", function() { + ok( false ); + }); + element.accordion( "option", "active", 1 ); +}); + +}( jQuery ) ); diff --git a/apps/it/static/js/ui/tests/unit/accordion/accordion_methods.js b/apps/it/static/js/ui/tests/unit/accordion/accordion_methods.js new file mode 100644 index 0000000..f81bd6d --- /dev/null +++ b/apps/it/static/js/ui/tests/unit/accordion/accordion_methods.js @@ -0,0 +1,126 @@ +(function( $ ) { + +var equalHeight = TestHelpers.accordion.equalHeight, + setupTeardown = TestHelpers.accordion.setupTeardown, + state = TestHelpers.accordion.state; + +module( "accordion: methods", setupTeardown() ); + +test( "destroy", function() { + expect( 1 ); + domEqual( "#list1", function() { + $( "#list1" ).accordion().accordion( "destroy" ); + }); +}); + +test( "enable/disable", function() { + expect( 4 ); + var element = $( "#list1" ).accordion(); + state( element, 1, 0, 0 ); + element.accordion( "disable" ); + // event does nothing + element.find( ".ui-accordion-header" ).eq( 1 ).trigger( "click" ); + state( element, 1, 0, 0 ); + // option still works + element.accordion( "option", "active", 1 ); + state( element, 0, 1, 0 ); + element.accordion( "enable" ); + element.accordion( "option", "active", 2 ); + state( element, 0, 0, 1 ); +}); + +test( "refresh", function() { + expect( 19 ); + var element = $( "#navigation" ) + .parent() + .height( 300 ) + .end() + .accordion({ + heightStyle: "fill" + }); + equalHeight( element, 255 ); + + element.parent().height( 500 ); + element.accordion( "refresh" ); + equalHeight( element, 455 ); + + element = $( "#list1" ); + element.accordion(); + state( element, 1, 0, 0 ); + + // disable panel via markup + element.find( "h3.bar" ).eq( 1 ).addClass( "ui-state-disabled" ); + element.accordion( "refresh" ); + state( element, 1, 0, 0 ); + + // don't add multiple icons + element.accordion( "refresh" ); + equal( element.find( ".ui-accordion-header-icon" ).length, 3 ); + + // add a panel + element + .append("<h3 class='bar' id='new_1'>new 1</h3>") + .append("<div class='foo' id='new_1_panel'>new 1</div>"); + element.accordion( "refresh" ); + state( element, 1, 0, 0, 0 ); + + // remove all tabs + element.find( "h3.bar, div.foo" ).remove(); + element.accordion( "refresh" ); + state( element ); + equal( element.accordion( "option", "active" ), false, "no active accordion panel" ); + + // add panels + element + .append("<h3 class='bar' id='new_2'>new 2</h3>") + .append("<div class='foo' id='new_2_panel'>new 2</div>") + .append("<h3 class='bar' id='new_3'>new 3</h3>") + .append("<div class='foo' id='new_3_panel'>new 3</div>") + .append("<h3 class='bar' id='new_4'>new 4</h3>") + .append("<div class='foo' id='new_4_panel'>new 4</div>") + .append("<h3 class='bar' id='new_5'>new 5</h3>") + .append("<div class='foo' id='new_5_panel'>new 5</div>"); + element.accordion( "refresh" ); + state( element, 1, 0, 0, 0 ); + + // activate third tab + element.accordion( "option", "active", 2 ); + state( element, 0, 0, 1, 0 ); + + // remove fourth panel, third panel should stay active + element.find( "h3.bar" ).eq( 3 ).remove(); + element.find( "div.foo" ).eq( 3 ).remove(); + element.accordion( "refresh" ); + state( element, 0, 0, 1 ); + + // remove third (active) panel, second panel should become active + element.find( "h3.bar" ).eq( 2 ).remove(); + element.find( "div.foo" ).eq( 2 ).remove(); + element.accordion( "refresh" ); + state( element, 0, 1 ); + + // remove first panel, previously active panel (now first) should stay active + element.find( "h3.bar" ).eq( 0 ).remove(); + element.find( "div.foo" ).eq( 0 ).remove(); + element.accordion( "refresh" ); + state( element, 1 ); + + // collapse all panels + element.accordion( "option", { + collapsible: true, + active: false + }); + state( element, 0 ); + element.accordion( "refresh" ); + state( element, 0 ); +}); + +test( "widget", function() { + expect( 2 ); + var element = $( "#list1" ).accordion(), + widgetElement = element.accordion( "widget" ); + equal( widgetElement.length, 1, "one element" ); + strictEqual( widgetElement[ 0 ], element[ 0 ], "same element" ); +}); + +}( jQuery ) ); diff --git a/apps/it/static/js/ui/tests/unit/accordion/accordion_options.js b/apps/it/static/js/ui/tests/unit/accordion/accordion_options.js new file mode 100644 index 0000000..214753e --- /dev/null +++ b/apps/it/static/js/ui/tests/unit/accordion/accordion_options.js @@ -0,0 +1,463 @@ +(function( $ ) { + +var equalHeight = TestHelpers.accordion.equalHeight, + setupTeardown = TestHelpers.accordion.setupTeardown, + state = TestHelpers.accordion.state; + +module( "accordion: options", setupTeardown() ); + +test( "{ active: default }", function() { + expect( 2 ); + var element = $( "#list1" ).accordion(); + equal( element.accordion( "option", "active" ), 0 ); + state( element, 1, 0, 0 ); +}); + +test( "{ active: null }", function() { + expect( 2 ); + var element = $( "#list1" ).accordion({ + active: null + }); + equal( element.accordion( "option", "active" ), 0 ); + state( element, 1, 0, 0 ); +}); + +test( "{ active: false }", function() { + expect( 7 ); + var element = $( "#list1" ).accordion({ + active: false, + collapsible: true + }); + state( element, 0, 0, 0 ); + equal( element.find( ".ui-accordion-header.ui-state-active" ).length, 0, "no headers selected" ); + equal( element.accordion( "option", "active" ), false ); + + element.accordion( "option", "collapsible", false ); + state( element, 1, 0, 0 ); + equal( element.accordion( "option", "active" ), 0 ); + + element.accordion( "destroy" ); + element.accordion({ + active: false + }); + state( element, 1, 0, 0 ); + strictEqual( element.accordion( "option", "active" ), 0 ); +}); + +test( "{ active: Number }", function() { + expect( 8 ); + var element = $( "#list1" ).accordion({ + active: 2 + }); + equal( element.accordion( "option", "active" ), 2 ); + state( element, 0, 0, 1 ); + + element.accordion( "option", "active", 0 ); + equal( element.accordion( "option", "active" ), 0 ); + state( element, 1, 0, 0 ); + + element.find( ".ui-accordion-header" ).eq( 1 ).click(); + equal( element.accordion( "option", "active" ), 1 ); + state( element, 0, 1, 0 ); + + element.accordion( "option", "active", 10 ); + equal( element.accordion( "option", "active" ), 1 ); + state( element, 0, 1, 0 ); +}); + +test( "{ active: -Number }", function() { + expect( 8 ); + var element = $( "#list1" ).accordion({ + active: -1 + }); + equal( element.accordion( "option", "active" ), 2 ); + state( element, 0, 0, 1 ); + + element.accordion( "option", "active", -2 ); + equal( element.accordion( "option", "active" ), 1 ); + state( element, 0, 1, 0 ); + + element.accordion( "option", "active", -10 ); + equal( element.accordion( "option", "active" ), 1 ); + state( element, 0, 1, 0 ); + + element.accordion( "option", "active", -3 ); + equal( element.accordion( "option", "active" ), 0 ); + state( element, 1, 0, 0 ); +}); + +test( "{ animate: false }", function() { + expect( 3 ); + var element = $( "#list1" ).accordion({ + animate: false + }), + panels = element.find( ".ui-accordion-content" ), + animate = $.fn.animate; + $.fn.animate = function() { + ok( false, ".animate() called" ); + }; + + ok( panels.eq( 0 ).is( ":visible" ), "first panel visible" ); + element.accordion( "option", "active", 1 ); + ok( panels.eq( 0 ).is( ":hidden" ), "first panel hidden" ); + ok( panels.eq( 1 ).is( ":visible" ), "second panel visible" ); + $.fn.animate = animate; +}); + +asyncTest( "{ animate: Number }", function() { + expect( 7 ); + var element = $( "#list1" ).accordion({ + animate: 100 + }), + panels = element.find( ".ui-accordion-content" ), + animate = $.fn.animate; + // called twice (both panels) + $.fn.animate = function( props, options ) { + equal( options.duration, 100, "correct duration" ); + equal( options.easing, undefined, "default easing" ); + animate.apply( this, arguments ); + }; + + ok( panels.eq( 0 ).is( ":visible" ), "first panel visible" ); + element.accordion( "option", "active", 1 ); + panels.promise().done(function() { + ok( panels.eq( 0 ).is( ":hidden" ), "first panel hidden" ); + ok( panels.eq( 1 ).is( ":visible" ), "second panel visible" ); + $.fn.animate = animate; + start(); + }); +}); + +asyncTest( "{ animate: String }", function() { + expect( 7 ); + var element = $( "#list1" ).accordion({ + animate: "linear" + }), + panels = element.find( ".ui-accordion-content" ), + animate = $.fn.animate; + // called twice (both panels) + $.fn.animate = function( props, options ) { + equal( options.duration, undefined, "default duration" ); + equal( options.easing, "linear", "correct easing" ); + animate.apply( this, arguments ); + }; + + ok( panels.eq( 0 ).is( ":visible" ), "first panel visible" ); + element.accordion( "option", "active", 1 ); + panels.promise().done(function() { + ok( panels.eq( 0 ).is( ":hidden" ), "first panel hidden" ); + ok( panels.eq( 1 ).is( ":visible" ), "second panel visible" ); + $.fn.animate = animate; + start(); + }); +}); + +asyncTest( "{ animate: {} }", function() { + expect( 7 ); + var element = $( "#list1" ).accordion({ + animate: {} + }), + panels = element.find( ".ui-accordion-content" ), + animate = $.fn.animate; + // called twice (both panels) + $.fn.animate = function( props, options ) { + equal( options.duration, undefined, "default duration" ); + equal( options.easing, undefined, "default easing" ); + animate.apply( this, arguments ); + }; + + ok( panels.eq( 0 ).is( ":visible" ), "first panel visible" ); + element.accordion( "option", "active", 1 ); + panels.promise().done(function() { + ok( panels.eq( 0 ).is( ":hidden" ), "first panel hidden" ); + ok( panels.eq( 1 ).is( ":visible" ), "second panel visible" ); + $.fn.animate = animate; + start(); + }); +}); + +asyncTest( "{ animate: { duration, easing } }", function() { + expect( 7 ); + var element = $( "#list1" ).accordion({ + animate: { duration: 100, easing: "linear" } + }), + panels = element.find( ".ui-accordion-content" ), + animate = $.fn.animate; + // called twice (both panels) + $.fn.animate = function( props, options ) { + equal( options.duration, 100, "correct duration" ); + equal( options.easing, "linear", "correct easing" ); + animate.apply( this, arguments ); + }; + + ok( panels.eq( 0 ).is( ":visible" ), "first panel visible" ); + element.accordion( "option", "active", 1 ); + panels.promise().done(function() { + ok( panels.eq( 0 ).is( ":hidden" ), "first panel hidden" ); + ok( panels.eq( 1 ).is( ":visible" ), "second panel visible" ); + $.fn.animate = animate; + start(); + }); +}); + +asyncTest( "{ animate: { duration, easing } }, animate down", function() { + expect( 7 ); + var element = $( "#list1" ).accordion({ + active: 1, + animate: { duration: 100, easing: "linear" } + }), + panels = element.find( ".ui-accordion-content" ), + animate = $.fn.animate; + // called twice (both panels) + $.fn.animate = function( props, options ) { + equal( options.duration, 100, "correct duration" ); + equal( options.easing, "linear", "correct easing" ); + animate.apply( this, arguments ); + }; + + ok( panels.eq( 1 ).is( ":visible" ), "first panel visible" ); + element.accordion( "option", "active", 0 ); + panels.promise().done(function() { + ok( panels.eq( 1 ).is( ":hidden" ), "first panel hidden" ); + ok( panels.eq( 0 ).is( ":visible" ), "second panel visible" ); + $.fn.animate = animate; + start(); + }); +}); + +asyncTest( "{ animate: { duration, easing, down } }, animate down", function() { + expect( 7 ); + var element = $( "#list1" ).accordion({ + active: 1, + animate: { + duration: 100, + easing: "linear", + down: { + easing: "swing" + } + } + }), + panels = element.find( ".ui-accordion-content" ), + animate = $.fn.animate; + // called twice (both panels) + $.fn.animate = function( props, options ) { + equal( options.duration, 100, "correct duration" ); + equal( options.easing, "swing", "correct easing" ); + animate.apply( this, arguments ); + }; + + ok( panels.eq( 1 ).is( ":visible" ), "first panel visible" ); + element.accordion( "option", "active", 0 ); + panels.promise().done(function() { + ok( panels.eq( 1 ).is( ":hidden" ), "first panel hidden" ); + ok( panels.eq( 0 ).is( ":visible" ), "second panel visible" ); + $.fn.animate = animate; + start(); + }); +}); + +test( "{ collapsible: false }", function() { + expect( 4 ); + var element = $( "#list1" ).accordion({ + active: 1 + }); + element.accordion( "option", "active", false ); + equal( element.accordion( "option", "active" ), 1 ); + state( element, 0, 1, 0 ); + + element.find( ".ui-accordion-header" ).eq( 1 ).click(); + equal( element.accordion( "option", "active" ), 1 ); + state( element, 0, 1, 0 ); +}); + +test( "{ collapsible: true }", function() { + expect( 6 ); + var element = $( "#list1" ).accordion({ + active: 1, + collapsible: true + }); + + element.accordion( "option", "active", false ); + equal( element.accordion( "option", "active" ), false ); + state( element, 0, 0, 0 ); + + element.accordion( "option", "active", 1 ); + equal( element.accordion( "option", "active" ), 1 ); + state( element, 0, 1, 0 ); + + element.find( ".ui-accordion-header" ).eq( 1 ).click(); + equal( element.accordion( "option", "active" ), false ); + state( element, 0, 0, 0 ); +}); + +test( "{ event: null }", function() { + expect( 5 ); + var element = $( "#list1" ).accordion({ + event: null + }); + state( element, 1, 0, 0 ); + + element.accordion( "option", "active", 1 ); + equal( element.accordion( "option", "active" ), 1 ); + state( element, 0, 1, 0 ); + + // ensure default click handler isn't bound + element.find( ".ui-accordion-header" ).eq( 2 ).click(); + equal( element.accordion( "option", "active" ), 1 ); + state( element, 0, 1, 0 ); +}); + +test( "{ event: custom }", function() { + expect( 11 ); + var element = $( "#list1" ).accordion({ + event: "custom1 custom2" + }); + state( element, 1, 0, 0 ); + + element.find( ".ui-accordion-header" ).eq( 1 ).trigger( "custom1" ); + equal( element.accordion( "option", "active" ), 1 ); + state( element, 0, 1, 0 ); + + // ensure default click handler isn't bound + element.find( ".ui-accordion-header" ).eq( 2 ).trigger( "click" ); + equal( element.accordion( "option", "active" ), 1 ); + state( element, 0, 1, 0 ); + + element.find( ".ui-accordion-header" ).eq( 2 ).trigger( "custom2" ); + equal( element.accordion( "option", "active" ), 2 ); + state( element, 0, 0, 1 ); + + element.accordion( "option", "event", "custom3" ); + + // ensure old event handlers are unbound + element.find( ".ui-accordion-header" ).eq( 1 ).trigger( "custom1" ); + element.find( ".ui-accordion-header" ).eq( 1 ).trigger( "custom2" ); + equal( element.accordion( "option", "active" ), 2 ); + state( element, 0, 0, 1 ); + + element.find( ".ui-accordion-header" ).eq( 1 ).trigger( "custom3" ); + equal( element.accordion( "option", "active" ), 1 ); + state( element, 0, 1, 0 ); +}); + +test( "{ header: default }", function() { + expect( 2 ); + // default: > li > :first-child,> :not(li):even + // > :not(li):even + state( $( "#list1" ).accordion(), 1, 0, 0); + // > li > :first-child + state( $( "#navigation" ).accordion(), 1, 0, 0); +}); + +test( "{ header: custom }", function() { + expect( 6 ); + var element = $( "#navigationWrapper" ).accordion({ + header: "h2" + }); + element.find( "h2" ).each(function() { + ok( $( this ).hasClass( "ui-accordion-header" ) ); + }); + equal( element.find( ".ui-accordion-header" ).length, 3 ); + state( element, 1, 0, 0 ); + element.accordion( "option", "active", 2 ); + state( element, 0, 0, 1 ); +}); + +test( "{ heightStyle: 'auto' }", function() { + expect( 3 ); + var element = $( "#navigation" ).accordion({ heightStyle: "auto" }); + equalHeight( element, 105 ); +}); + +test( "{ heightStyle: 'content' }", function() { + expect( 3 ); + var element = $( "#navigation" ).accordion({ heightStyle: "content" }), + sizes = element.find( ".ui-accordion-content" ).map(function() { + return $( this ).height(); + }).get(); + equal( sizes[ 0 ], 75 ); + equal( sizes[ 1 ], 105 ); + equal( sizes[ 2 ], 45 ); +}); + +test( "{ heightStyle: 'fill' }", function() { + expect( 3 ); + $( "#navigationWrapper" ).height( 500 ); + var element = $( "#navigation" ).accordion({ heightStyle: "fill" }); + equalHeight( element, 455 ); +}); + +test( "{ heightStyle: 'fill' } with sibling", function() { + expect( 3 ); + $( "#navigationWrapper" ).height( 500 ); + $( "<p>Lorem Ipsum</p>" ) + .css({ + height: 50, + marginTop: 20, + marginBottom: 30 + }) + .prependTo( "#navigationWrapper" ); + var element = $( "#navigation" ).accordion({ heightStyle: "fill" }); + equalHeight( element , 355 ); +}); + +test( "{ heightStyle: 'fill' } with multiple siblings", function() { + expect( 3 ); + $( "#navigationWrapper" ).height( 500 ); + $( "<p>Lorem Ipsum</p>" ) + .css({ + height: 50, + marginTop: 20, + marginBottom: 30 + }) + .prependTo( "#navigationWrapper" ); + $( "<p>Lorem Ipsum</p>" ) + .css({ + height: 50, + marginTop: 20, + marginBottom: 30, + position: "absolute" + }) + .prependTo( "#navigationWrapper" ); + $( "<p>Lorem Ipsum</p>" ) + .css({ + height: 25, + marginTop: 10, + marginBottom: 15 + }) + .prependTo( "#navigationWrapper" ); + var element = $( "#navigation" ).accordion({ heightStyle: "fill" }); + equalHeight( element, 305 ); +}); + +test( "{ icons: false }", function() { + expect( 8 ); + var element = $( "#list1" ); + function icons( on ) { + deepEqual( element.find( "span.ui-icon").length, on ? 3 : 0 ); + deepEqual( element.find( ".ui-accordion-header.ui-accordion-icons" ).length, on ? 3 : 0 ); + } + element.accordion(); + icons( true ); + element.accordion( "destroy" ).accordion({ + icons: false + }); + icons( false ); + element.accordion( "option", "icons", { header: "foo", activeHeader: "bar" } ); + icons( true ); + element.accordion( "option", "icons", false ); + icons( false ); +}); + +test( "{ icons: hash }", function() { + expect( 3 ); + var element = $( "#list1" ).accordion({ + icons: { activeHeader: "a1", header: "h1" } + }); + ok( element.find( ".ui-accordion-header.ui-state-active span.ui-icon" ).hasClass( "a1" ) ); + element.accordion( "option", "icons", { activeHeader: "a2", header: "h2" } ); + ok( !element.find( ".ui-accordion-header.ui-state-active span.ui-icon" ).hasClass( "a1" ) ); + ok( element.find( ".ui-accordion-header.ui-state-active span.ui-icon" ).hasClass( "a2" ) ); +}); + +}( jQuery ) ); diff --git a/apps/it/static/js/ui/tests/unit/accordion/accordion_test_helpers.js b/apps/it/static/js/ui/tests/unit/accordion/accordion_test_helpers.js new file mode 100644 index 0000000..643f8e2 --- /dev/null +++ b/apps/it/static/js/ui/tests/unit/accordion/accordion_test_helpers.js @@ -0,0 +1,27 @@ +TestHelpers.accordion = { + equalHeight: function( accordion, height ) { + accordion.find( ".ui-accordion-content" ).each(function() { + equal( $( this ).outerHeight(), height ); + }); + }, + + setupTeardown: function() { + var animate = $.ui.accordion.prototype.options.animate; + return { + setup: function() { + $.ui.accordion.prototype.options.animate = false; + }, + teardown: function() { + $.ui.accordion.prototype.options.animate = animate; + } + }; + }, + + state: function( accordion ) { + var expected = $.makeArray( arguments ).slice( 1 ), + actual = accordion.find( ".ui-accordion-content" ).map(function() { + return $( this ).css( "display" ) === "none" ? 0 : 1; + }).get(); + QUnit.push( QUnit.equiv(actual, expected), actual, expected ); + } +}; diff --git a/apps/it/static/js/ui/tests/unit/accordion/all.html b/apps/it/static/js/ui/tests/unit/accordion/all.html new file mode 100644 index 0000000..8710d03 --- /dev/null +++ b/apps/it/static/js/ui/tests/unit/accordion/all.html @@ -0,0 +1,30 @@ +<!doctype html> +<html lang="en"> +<head> + <meta charset="utf-8"> + <title>jQuery UI Accordion Test Suite</title> + + <script src="../../../jquery-1.10.2.js"></script> + + <link rel="stylesheet" href="../../../external/qunit.css"> + <link rel="stylesheet" href="../qunit-composite.css"> + <script src="../../../external/qunit.js"></script> + <script src="../qunit-composite.js"></script> + <script src="../subsuite.js"></script> + + <script> + testAllVersions( "accordion" ); + </script> +</head> +<body> + +<h1 id="qunit-header">jQuery UI Accordion Test Suite</h1> +<h2 id="qunit-banner"></h2> +<div id="qunit-testrunner-toolbar"></div> +<h2 id="qunit-userAgent"></h2> +<ol id="qunit-tests"></ol> +<div id="qunit-fixture"> + +</div> +</body> +</html> diff --git a/apps/it/static/js/ui/tests/unit/all.html b/apps/it/static/js/ui/tests/unit/all.html new file mode 100644 index 0000000..a05deb1 --- /dev/null +++ b/apps/it/static/js/ui/tests/unit/all.html @@ -0,0 +1,70 @@ +<!doctype html> +<html lang="en"> +<head> + <meta charset="utf-8"> + <title>jQuery UI Test Suite</title> + + <script src="../../jquery-1.10.2.js"></script> + + <link rel="stylesheet" href="../../external/qunit.css"> + <link rel="stylesheet" href="qunit-composite.css"> + <script src="../../external/qunit.js"></script> + <script src="qunit-composite.js"></script> + + <script> + (function() { + + var params = [], + suites = [ + "accordion/accordion.html", + "autocomplete/autocomplete.html", + "button/button.html", + "core/core.html", + "datepicker/datepicker.html", + "dialog/dialog.html", + "dialog/dialog_deprecated.html", + "draggable/draggable.html", + "droppable/droppable.html", + "effects/effects.html", + "menu/menu.html", + "position/position.html", + "progressbar/progressbar.html", + "resizable/resizable.html", + "selectable/selectable.html", + "slider/slider.html", + "sortable/sortable.html", + "spinner/spinner.html", + "tabs/tabs.html", + "tooltip/tooltip.html", + "widget/widget.html" + ]; + + $.each( QUnit.urlParams, function( key, value ) { + if ( key === "filter" ) { + return; + } + params.push( encodeURIComponent( key ) + "=" + encodeURIComponent( value ) ); + }); + if ( params.length ) { + params = "?" + params.join( "&" ); + suites = $.map( suites, function( suite ) { + return suite + params; + }); + } + QUnit.testSuites( suites ); + + }()); + </script> +</head> +<body> + +<h1 id="qunit-header">jQuery UI Test Suite</h1> +<h2 id="qunit-banner"></h2> +<div id="qunit-testrunner-toolbar"></div> +<h2 id="qunit-userAgent"></h2> +<ol id="qunit-tests"></ol> +<div id="qunit-fixture"> + +</div> +</body> +</html> diff --git a/apps/it/static/js/ui/tests/unit/autocomplete/all.html b/apps/it/static/js/ui/tests/unit/autocomplete/all.html new file mode 100644 index 0000000..bfc8b8e --- /dev/null +++ b/apps/it/static/js/ui/tests/unit/autocomplete/all.html @@ -0,0 +1,30 @@ +<!doctype html> +<html lang="en"> +<head> + <meta charset="utf-8"> + <title>jQuery UI Autocomplete Test Suite</title> + + <script src="../../../jquery-1.10.2.js"></script> + + <link rel="stylesheet" href="../../../external/qunit.css"> + <link rel="stylesheet" href="../qunit-composite.css"> + <script src="../../../external/qunit.js"></script> + <script src="../qunit-composite.js"></script> + <script src="../subsuite.js"></script> + + <script> + testAllVersions( "autocomplete" ); + </script> +</head> +<body> + +<h1 id="qunit-header">jQuery UI Autocomplete Test Suite</h1> +<h2 id="qunit-banner"></h2> +<div id="qunit-testrunner-toolbar"></div> +<h2 id="qunit-userAgent"></h2> +<ol id="qunit-tests"></ol> +<div id="qunit-fixture"> + +</div> +</body> +</html> diff --git a/apps/it/static/js/ui/tests/unit/autocomplete/autocomplete.html b/apps/it/static/js/ui/tests/unit/autocomplete/autocomplete.html new file mode 100644 index 0000000..8c7b5b6 --- /dev/null +++ b/apps/it/static/js/ui/tests/unit/autocomplete/autocomplete.html @@ -0,0 +1,49 @@ +<!doctype html> +<html lang="en"> +<head> + <meta charset="utf-8"> + <title>jQuery UI Autocomplete Test Suite</title> + + <script src="../../jquery.js"></script> + <link rel="stylesheet" href="../../../external/qunit.css"> + <script src="../../../external/qunit.js"></script> + <script src="../../jquery.simulate.js"></script> + <script src="../testsuite.js"></script> + <script> + TestHelpers.loadResources({ + css: [ "ui.core", "ui.menu", "ui.autocomplete" ], + js: [ + "ui/jquery.ui.core.js", + "ui/jquery.ui.widget.js", + "ui/jquery.ui.position.js", + "ui/jquery.ui.menu.js", + "ui/jquery.ui.autocomplete.js" + ] + }); + </script> + + <script src="autocomplete_common.js"></script> + <script src="autocomplete_core.js"></script> + <script src="autocomplete_events.js"></script> + <script src="autocomplete_methods.js"></script> + <script src="autocomplete_options.js"></script> + + <script src="../swarminject.js"></script> +</head> +<body> + +<h1 id="qunit-header">jQuery UI Autocomplete Test Suite</h1> +<h2 id="qunit-banner"></h2> +<div id="qunit-testrunner-toolbar"></div> +<h2 id="qunit-userAgent"></h2> +<ol id="qunit-tests"></ol> +<div id="qunit-fixture"> + +<div id="autocomplete-wrap1" class="autocomplete-wrap"></div> +<div id="autocomplete-wrap2" class="autocomplete-wrap"><input id="autocomplete" class="foo"></div> +<div id="autocomplete-contenteditable" contenteditable="" tabindex=0></div> +<textarea id="autocomplete-textarea"></textarea> + +</div> +</body> +</html> diff --git a/apps/it/static/js/ui/tests/unit/autocomplete/autocomplete_common.js b/apps/it/static/js/ui/tests/unit/autocomplete/autocomplete_common.js new file mode 100644 index 0000000..63b24d3 --- /dev/null +++ b/apps/it/static/js/ui/tests/unit/autocomplete/autocomplete_common.js @@ -0,0 +1,29 @@ +TestHelpers.commonWidgetTests( "autocomplete", { + defaults: { + appendTo: null, + autoFocus: false, + delay: 300, + disabled: false, + messages: { + noResults: "No search results.", + results: $.ui.autocomplete.prototype.options.messages.results + }, + minLength: 1, + position: { + my: "left top", + at: "left bottom", + collision: "none" + }, + source: null, + + // callbacks + change: null, + close: null, + create: null, + focus: null, + open: null, + response: null, + search: null, + select: null + } +}); diff --git a/apps/it/static/js/ui/tests/unit/autocomplete/autocomplete_core.js b/apps/it/static/js/ui/tests/unit/autocomplete/autocomplete_core.js new file mode 100644 index 0000000..679955d --- /dev/null +++ b/apps/it/static/js/ui/tests/unit/autocomplete/autocomplete_core.js @@ -0,0 +1,270 @@ +(function( $ ) { + +module( "autocomplete: core" ); + +test( "prevent form submit on enter when menu is active", function() { + expect( 2 ); + var event, + element = $( "#autocomplete" ) + .autocomplete({ + source: [ "java", "javascript" ] + }) + .val( "ja" ) + .autocomplete( "search" ), + menu = element.autocomplete( "widget" ); + + event = $.Event( "keydown" ); + event.keyCode = $.ui.keyCode.DOWN; + element.trigger( event ); + deepEqual( menu.find( ".ui-menu-item:has(.ui-state-focus)" ).length, 1, "menu item is active" ); + + event = $.Event( "keydown" ); + event.keyCode = $.ui.keyCode.ENTER; + element.trigger( event ); + ok( event.isDefaultPrevented(), "default action is prevented" ); +}); + +test( "allow form submit on enter when menu is not active", function() { + expect( 1 ); + var event, + element = $( "#autocomplete" ) + .autocomplete({ + autoFocus: false, + source: [ "java", "javascript" ] + }) + .val( "ja" ) + .autocomplete( "search" ); + + event = $.Event( "keydown" ); + event.keyCode = $.ui.keyCode.ENTER; + element.trigger( event ); + ok( !event.isDefaultPrevented(), "default action is prevented" ); +}); + +(function() { + test( "up arrow invokes search - input", function() { + arrowsInvokeSearch( "#autocomplete", true, true ); + }); + + test( "down arrow invokes search - input", function() { + arrowsInvokeSearch( "#autocomplete", false, true ); + }); + + test( "up arrow invokes search - textarea", function() { + arrowsInvokeSearch( "#autocomplete-textarea", true, false ); + }); + + test( "down arrow invokes search - textarea", function() { + arrowsInvokeSearch( "#autocomplete-textarea", false, false ); + }); + + test( "up arrow invokes search - contenteditable", function() { + arrowsInvokeSearch( "#autocomplete-contenteditable", true, false ); + }); + + test( "down arrow invokes search - contenteditable", function() { + arrowsInvokeSearch( "#autocomplete-contenteditable", false, false ); + }); + + test( "up arrow moves focus - input", function() { + arrowsMoveFocus( "#autocomplete", true ); + }); + + test( "down arrow moves focus - input", function() { + arrowsMoveFocus( "#autocomplete", false ); + }); + + test( "up arrow moves focus - textarea", function() { + arrowsMoveFocus( "#autocomplete-textarea", true ); + }); + + test( "down arrow moves focus - textarea", function() { + arrowsMoveFocus( "#autocomplete-textarea", false ); + }); + + test( "up arrow moves focus - contenteditable", function() { + arrowsMoveFocus( "#autocomplete-contenteditable", true ); + }); + + test( "down arrow moves focus - contenteditable", function() { + arrowsMoveFocus( "#autocomplete-contenteditable", false ); + }); + + test( "up arrow moves cursor - input", function() { + arrowsNavigateElement( "#autocomplete", true, false ); + }); + + test( "down arrow moves cursor - input", function() { + arrowsNavigateElement( "#autocomplete", false, false ); + }); + + test( "up arrow moves cursor - textarea", function() { + arrowsNavigateElement( "#autocomplete-textarea", true, true ); + }); + + test( "down arrow moves cursor - textarea", function() { + arrowsNavigateElement( "#autocomplete-textarea", false, true ); + }); + + test( "up arrow moves cursor - contenteditable", function() { + arrowsNavigateElement( "#autocomplete-contenteditable", true, true ); + }); + + test( "down arrow moves cursor - contenteditable", function() { + arrowsNavigateElement( "#autocomplete-contenteditable", false, true ); + }); + + function arrowsInvokeSearch( id, isKeyUp, shouldMove ) { + expect( 1 ); + + var didMove = false, + element = $( id ).autocomplete({ + source: [ "a" ], + delay: 0, + minLength: 0 + }); + element.data( "ui-autocomplete" )._move = function() { + didMove = true; + }; + element.simulate( "keydown", { keyCode: ( isKeyUp ? $.ui.keyCode.UP : $.ui.keyCode.DOWN ) } ); + equal( didMove, shouldMove, "respond to arrow" ); + } + + function arrowsMoveFocus( id, isKeyUp ) { + expect( 1 ); + + var element = $( id ).autocomplete({ + source: [ "a" ], + delay: 0, + minLength: 0 + }); + element.data( "ui-autocomplete" )._move = function() { + ok( true, "repsond to arrow" ); + }; + element.autocomplete( "search" ); + element.simulate( "keydown", { keyCode: ( isKeyUp ? $.ui.keyCode.UP : $.ui.keyCode.DOWN ) } ); + } + + function arrowsNavigateElement( id, isKeyUp, shouldMove ) { + expect( 1 ); + + var didMove = false, + element = $( id ).autocomplete({ + source: [ "a" ], + delay: 0, + minLength: 0 + }); + element.on( "keypress", function( e ) { + didMove = !e.isDefaultPrevented(); + }); + element.simulate( "keydown", { keyCode: ( isKeyUp ? $.ui.keyCode.UP : $.ui.keyCode.DOWN ) } ); + element.simulate( "keypress" ); + equal( didMove, shouldMove, "respond to arrow" ); + } +})(); + +asyncTest( "handle race condition", function() { + expect( 3 ); + var count = 0, + element = $( "#autocomplete" ).autocomplete({ + source: function( request, response ) { + count++; + if ( request.term.length === 1 ) { + equal( count, 1, "request with 1 character is first" ); + setTimeout(function() { + response([ "one" ]); + setTimeout( checkResults, 1 ); + }, 1 ); + return; + } + equal( count, 2, "request with 2 characters is second" ); + response([ "two" ]); + } + }); + + element.autocomplete( "search", "a" ); + element.autocomplete( "search", "ab" ); + + function checkResults() { + equal( element.autocomplete( "widget" ).find( ".ui-menu-item" ).text(), "two", + "correct results displayed" ); + start(); + } +}); + +asyncTest( "simultaneous searches (#9334)", function() { + expect( 2 ); + var element = $( "#autocomplete" ).autocomplete({ + source: function( request, response ) { + setTimeout(function() { + response([ request.term ]); + }); + }, + response: function() { + ok( true, "response from first instance" ); + } + }), + element2 = $( "#autocomplete-textarea" ).autocomplete({ + source: function( request, response ) { + setTimeout(function() { + response([ request.term ]); + }); + }, + response: function() { + ok( true, "response from second instance" ); + start(); + } + }); + + element.autocomplete( "search", "test" ); + element2.autocomplete( "search", "test" ); +}); + +test( "ARIA", function() { + expect( 7 ); + var element = $( "#autocomplete" ).autocomplete({ + source: [ "java", "javascript" ] + }), + liveRegion = element.data( "ui-autocomplete" ).liveRegion; + + equal( liveRegion.text(), "", "Empty live region on create" ); + + element.autocomplete( "search", "j" ); + equal( liveRegion.text(), "2 results are available, use up and down arrow keys to navigate.", + "Live region for multiple values" ); + + element.simulate( "keydown", { keyCode: $.ui.keyCode.DOWN } ); + equal( liveRegion.text(), "2 results are available, use up and down arrow keys to navigate.", + "Live region not changed on focus" ); + + element.one( "autocompletefocus", function( event ) { + event.preventDefault(); + }); + element.simulate( "keydown", { keyCode: $.ui.keyCode.DOWN } ); + equal( liveRegion.text(), "javascript", + "Live region updated when default focus is prevented" ); + + element.autocomplete( "search", "javas" ); + equal( liveRegion.text(), "1 result is available, use up and down arrow keys to navigate.", + "Live region for one value" ); + + element.autocomplete( "search", "z" ); + equal( liveRegion.text(), "No search results.", + "Live region for no values" ); + + element.autocomplete( "search", "j" ); + equal( liveRegion.text(), "2 results are available, use up and down arrow keys to navigate.", + "Live region for multiple values" ); +}); + +test( ".replaceWith() (#9172)", function() { + expect( 1 ); + + var element = $( "#autocomplete" ).autocomplete(), + replacement = "<div>test</div>", + parent = element.parent(); + element.replaceWith( replacement ); + equal( parent.html().toLowerCase(), replacement ); +}); + +}( jQuery ) ); diff --git a/apps/it/static/js/ui/tests/unit/autocomplete/autocomplete_events.js b/apps/it/static/js/ui/tests/unit/autocomplete/autocomplete_events.js new file mode 100644 index 0000000..fb1cf73 --- /dev/null +++ b/apps/it/static/js/ui/tests/unit/autocomplete/autocomplete_events.js @@ -0,0 +1,182 @@ +(function( $ ) { + +module( "autocomplete: events" ); + +var data = [ "Clojure", "COBOL", "ColdFusion", "Java", "JavaScript", "Scala", "Scheme" ]; + +$.each([ + { + type: "input", + selector: "#autocomplete", + valueMethod: "val" + }, + { + type: "textarea", + selector: "#autocomplete-textarea", + valueMethod: "val" + }, + { + type: "contenteditable", + selector: "#autocomplete-contenteditable", + valueMethod: "text" + } +], function( i, settings ) { + asyncTest( "all events - " + settings.type, function() { + expect( 13 ); + var element = $( settings.selector ) + .autocomplete({ + autoFocus: false, + delay: 0, + source: data, + search: function( event ) { + equal( event.originalEvent.type, "keydown", "search originalEvent" ); + }, + response: function( event, ui ) { + deepEqual( ui.content, [ + { label: "Clojure", value: "Clojure" }, + { label: "Java", value: "Java" }, + { label: "JavaScript", value: "JavaScript" } + ], "response ui.content" ); + ui.content.splice( 0, 1 ); + }, + open: function() { + ok( menu.is( ":visible" ), "menu open on open" ); + }, + focus: function( event, ui ) { + equal( event.originalEvent.type, "menufocus", "focus originalEvent" ); + deepEqual( ui.item, { label: "Java", value: "Java" }, "focus ui.item" ); + }, + close: function( event ) { + equal( event.originalEvent.type, "menuselect", "close originalEvent" ); + ok( menu.is( ":hidden" ), "menu closed on close" ); + }, + select: function( event, ui ) { + equal( event.originalEvent.type, "menuselect", "select originalEvent" ); + deepEqual( ui.item, { label: "Java", value: "Java" }, "select ui.item" ); + }, + change: function( event, ui ) { + equal( event.originalEvent.type, "blur", "change originalEvent" ); + deepEqual( ui.item, { label: "Java", value: "Java" }, "change ui.item" ); + ok( menu.is( ":hidden" ), "menu closed on change" ); + start(); + } + }), + menu = element.autocomplete( "widget" ); + + element.simulate( "focus" )[ settings.valueMethod ]( "j" ).keydown(); + setTimeout(function() { + ok( menu.is( ":visible" ), "menu is visible after delay" ); + element.simulate( "keydown", { keyCode: $.ui.keyCode.DOWN } ); + element.simulate( "keydown", { keyCode: $.ui.keyCode.ENTER } ); + // blur must be async for IE to handle it properly + setTimeout(function() { + element.simulate( "blur" ); + }, 1 ); + }, 50 ); + }); +}); + +asyncTest( "change without selection", function() { + expect( 1 ); + var element = $( "#autocomplete" ).autocomplete({ + delay: 0, + source: data, + change: function( event, ui ) { + strictEqual( ui.item, null ); + start(); + } + }); + element.triggerHandler( "focus" ); + element.val( "ja" ).triggerHandler( "blur" ); +}); + +asyncTest( "cancel search", function() { + expect( 6 ); + var first = true, + element = $( "#autocomplete" ).autocomplete({ + delay: 0, + source: data, + search: function() { + if ( first ) { + equal( element.val(), "ja", "val on first search" ); + first = false; + return false; + } + equal( element.val(), "java", "val on second search" ); + }, + open: function() { + ok( true, "menu opened" ); + } + }), + menu = element.autocomplete( "widget" ); + element.val( "ja" ).keydown(); + setTimeout(function() { + ok( menu.is( ":hidden" ), "menu is hidden after first search" ); + element.val( "java" ).keydown(); + setTimeout(function() { + ok( menu.is( ":visible" ), "menu is visible after second search" ); + equal( menu.find( ".ui-menu-item" ).length, 2, "# of menu items" ); + start(); + }, 50 ); + }, 50 ); +}); + +asyncTest( "cancel focus", function() { + expect( 1 ); + var customVal = "custom value", + element = $( "#autocomplete" ).autocomplete({ + delay: 0, + source: data, + focus: function() { + $( this ).val( customVal ); + return false; + } + }); + element.val( "ja" ).keydown(); + setTimeout(function() { + element.simulate( "keydown", { keyCode: $.ui.keyCode.DOWN } ); + equal( element.val(), customVal ); + start(); + }, 50 ); +}); + +asyncTest( "cancel select", function() { + expect( 1 ); + var customVal = "custom value", + element = $( "#autocomplete" ).autocomplete({ + delay: 0, + source: data, + select: function() { + $( this ).val( customVal ); + return false; + } + }); + element.val( "ja" ).keydown(); + setTimeout(function() { + element.simulate( "keydown", { keyCode: $.ui.keyCode.DOWN } ); + element.simulate( "keydown", { keyCode: $.ui.keyCode.ENTER } ); + equal( element.val(), customVal ); + start(); + }, 50 ); +}); + +asyncTest( "blur during remote search", function() { + expect( 1 ); + var ac = $( "#autocomplete" ).autocomplete({ + delay: 0, + source: function( request, response ) { + ok( true, "trigger request" ); + ac.simulate( "blur" ); + setTimeout(function() { + response([ "result" ]); + start(); + }, 100 ); + }, + open: function() { + ok( false, "opened after a blur" ); + } + }); + ac.val( "ro" ).keydown(); +}); + +}( jQuery ) ); diff --git a/apps/it/static/js/ui/tests/unit/autocomplete/autocomplete_methods.js b/apps/it/static/js/ui/tests/unit/autocomplete/autocomplete_methods.js new file mode 100644 index 0000000..1bfa105 --- /dev/null +++ b/apps/it/static/js/ui/tests/unit/autocomplete/autocomplete_methods.js @@ -0,0 +1,45 @@ +(function( $ ) { + +module( "autocomplete: methods" ); + +test( "destroy", function() { + expect( 1 ); + domEqual( "#autocomplete", function() { + $( "#autocomplete" ).autocomplete().autocomplete( "destroy" ); + }); +}); + +test( "search, close", function() { + expect( 6 ); + var data = [ "c++", "java", "php", "coldfusion", "javascript", "asp", "ruby", "python", "c", "scala", "groovy", "haskell", "perl" ], + element = $( "#autocomplete" ).autocomplete({ + source: data, + minLength: 0 + }), + menu = element.autocomplete( "widget" ); + + ok( menu.is( ":hidden" ), "menu is hidden on init" ); + + element.autocomplete( "search" ); + ok( menu.is( ":visible" ), "menu is visible after search" ); + equal( menu.find( ".ui-menu-item" ).length, data.length, "all items for a blank search" ); + + element.val( "has" ).autocomplete( "search" ); + equal( menu.find( ".ui-menu-item" ).text(), "haskell", "only one item for set input value" ); + + element.autocomplete( "search", "ja" ); + equal( menu.find( ".ui-menu-item" ).length, 2, "only java and javascript for 'ja'" ); + + element.autocomplete( "close" ); + ok( menu.is( ":hidden" ), "menu is hidden after close" ); +}); + +test( "widget", function() { + expect( 2 ); + var element = $( "#autocomplete" ).autocomplete(), + widgetElement = element.autocomplete( "widget" ); + equal( widgetElement.length, 1, "one element" ); + ok( widgetElement.is( ".ui-menu" ), "menu element" ); +}); + +}( jQuery ) ); diff --git a/apps/it/static/js/ui/tests/unit/autocomplete/autocomplete_options.js b/apps/it/static/js/ui/tests/unit/autocomplete/autocomplete_options.js new file mode 100644 index 0000000..0a2e07b --- /dev/null +++ b/apps/it/static/js/ui/tests/unit/autocomplete/autocomplete_options.js @@ -0,0 +1,278 @@ +(function( $ ) { + +module( "autocomplete: options" ); + +var data = [ "c++", "java", "php", "coldfusion", "javascript", "asp", "ruby", + "python", "c", "scala", "groovy", "haskell", "perl" ]; + +test( "appendTo", function() { + expect( 8 ); + var detached = $( "<div>" ), + element = $( "#autocomplete" ).autocomplete(); + equal( element.autocomplete( "widget" ).parent()[ 0 ], document.body, + "defaults to body" ); + element.autocomplete( "destroy" ); + + element.autocomplete({ + appendTo: ".autocomplete-wrap" + }); + equal( element.autocomplete( "widget" ).parent()[ 0 ], + $( "#autocomplete-wrap1" )[ 0 ], "first found element" ); + equal( $( "#autocomplete-wrap2 .ui-autocomplete" ).length, 0, + "only appends to one element" ); + element.autocomplete( "destroy" ); + + $( "#autocomplete-wrap2" ).addClass( "ui-front" ); + element.autocomplete(); + equal( element.autocomplete( "widget" ).parent()[ 0 ], + $( "#autocomplete-wrap2" )[ 0 ], "null, inside .ui-front" ); + element.autocomplete( "destroy" ); + $( "#autocomlete-wrap2" ).removeClass( "ui-front" ); + + element.autocomplete().autocomplete( "option", "appendTo", "#autocomplete-wrap1" ); + equal( element.autocomplete( "widget" ).parent()[ 0 ], + $( "#autocomplete-wrap1" )[ 0 ], "modified after init" ); + element.autocomplete( "destroy" ); + + element.autocomplete({ + appendTo: detached + }); + equal( element.autocomplete( "widget" ).parent()[ 0 ], detached[ 0 ], + "detached jQuery object" ); + element.autocomplete( "destroy" ); + + element.autocomplete({ + appendTo: detached[ 0 ] + }); + equal( element.autocomplete( "widget" ).parent()[ 0 ], detached[ 0 ], + "detached DOM element" ); + element.autocomplete( "destroy" ); + + element.autocomplete().autocomplete( "option", "appendTo", detached ); + equal( element.autocomplete( "widget" ).parent()[ 0 ], detached[ 0 ], + "detached DOM element via option()" ); + element.autocomplete( "destroy" ); +}); + +function autoFocusTest( afValue, focusedLength ) { + var element = $( "#autocomplete" ).autocomplete({ + autoFocus: afValue, + delay: 0, + source: data, + open: function() { + equal( + element.autocomplete( "widget" ) + .children( ".ui-menu-item:first" ) + .find( ".ui-state-focus" ) + .length, + focusedLength, + "first item is " + (afValue ? "" : "not") + " auto focused" ); + start(); + } + }); + element.val( "ja" ).keydown(); + stop(); +} + +test( "autoFocus: false", function() { + expect( 1 ); + autoFocusTest( false, 0 ); +}); + +test( "autoFocus: true", function() { + expect( 1 ); + autoFocusTest( true, 1 ); +}); + +asyncTest( "delay", function() { + expect( 2 ); + var element = $( "#autocomplete" ).autocomplete({ + source: data, + delay: 50 + }), + menu = element.autocomplete( "widget" ); + element.val( "ja" ).keydown(); + + ok( menu.is( ":hidden" ), "menu is closed immediately after search" ); + + setTimeout(function() { + ok( menu.is( ":visible" ), "menu is open after delay" ); + start(); + }, 100 ); +}); + +asyncTest( "disabled", function() { + expect( 2 ); + var element = $( "#autocomplete" ).autocomplete({ + source: data, + delay: 0, + disabled: true + }), + menu = element.autocomplete( "widget" ); + element.val( "ja" ).keydown(); + + ok( menu.is( ":hidden" ) ); + + setTimeout(function() { + ok( menu.is( ":hidden" ) ); + start(); + }, 50 ); +}); + +test( "minLength", function() { + expect( 2 ); + var element = $( "#autocomplete" ).autocomplete({ + source: data + }), + menu = element.autocomplete( "widget" ); + element.autocomplete( "search", "" ); + ok( menu.is( ":hidden" ), "blank not enough for minLength: 1" ); + + element.autocomplete( "option", "minLength", 0 ); + element.autocomplete( "search", "" ); + ok( menu.is( ":visible" ), "blank enough for minLength: 0" ); +}); + +asyncTest( "minLength, exceed then drop below", function() { + expect( 4 ); + var element = $( "#autocomplete" ).autocomplete({ + minLength: 2, + source: function( req, res ) { + equal( req.term, "12", "correct search term" ); + setTimeout(function() { + res([ "item" ]); + }, 1 ); + } + }), + menu = element.autocomplete( "widget" ); + + ok( menu.is( ":hidden" ), "menu is hidden before first search" ); + element.autocomplete( "search", "12" ); + + ok( menu.is( ":hidden" ), "menu is hidden before second search" ); + element.autocomplete( "search", "1" ); + + setTimeout(function() { + ok( menu.is( ":hidden" ), "menu is hidden after searches" ); + start(); + }, 50 ); +}); + +test( "minLength, exceed then drop below then exceed", function() { + expect( 3 ); + var _res = [], + element = $( "#autocomplete" ).autocomplete({ + minLength: 2, + source: function( req, res ) { + _res.push( res ); + } + }), + menu = element.autocomplete( "widget" ); + + // trigger a valid search + ok( menu.is( ":hidden" ), "menu is hidden before first search" ); + element.autocomplete( "search", "12" ); + + // trigger a search below the minLength, to turn on cancelSearch flag + ok( menu.is( ":hidden" ), "menu is hidden before second search" ); + element.autocomplete( "search", "1" ); + + // trigger a valid search + element.autocomplete( "search", "13" ); + // react as if the first search was cancelled (default ajax behavior) + _res[ 0 ]([]); + // react to second search + _res[ 1 ]([ "13" ]); + + ok( menu.is( ":visible" ), "menu is visible after searches" ); +}); + +test( "source, local string array", function() { + expect( 1 ); + var element = $( "#autocomplete" ).autocomplete({ + source: data + }), + menu = element.autocomplete( "widget" ); + element.val( "ja" ).autocomplete( "search" ); + equal( menu.find( ".ui-menu-item" ).text(), "javajavascript" ); +}); + +function sourceTest( source, async ) { + var element = $( "#autocomplete" ).autocomplete({ + source: source + }), + menu = element.autocomplete( "widget" ); + function result() { + equal( menu.find( ".ui-menu-item" ).text(), "javajavascript" ); + element.autocomplete( "destroy" ); + if ( async ) { + start(); + } + } + if ( async ) { + stop(); + $( document ).one( "ajaxStop", result ); + } + element.val( "ja" ).autocomplete( "search" ); + if ( !async ) { + result(); + } +} + +test( "source, local object array, only label property", function() { + expect( 1 ); + sourceTest([ + { label: "java" }, + { label: "php" }, + { label: "coldfusion" }, + { label: "javascript" } + ]); +}); + +test( "source, local object array, only value property", function() { + expect( 1 ); + sourceTest([ + { value: "java" }, + { value: "php" }, + { value: "coldfusion" }, + { value: "javascript" } + ]); +}); + +test( "source, url string with remote json string array", function() { + expect( 1 ); + sourceTest( "remote_string_array.txt", true ); +}); + +test( "source, url string with remote json object array, only value properties", function() { + expect( 1 ); + sourceTest( "remote_object_array_values.txt", true ); +}); + +test( "source, url string with remote json object array, only label properties", function() { + expect( 1 ); + sourceTest( "remote_object_array_labels.txt", true ); +}); + +test( "source, custom", function() { + expect( 2 ); + sourceTest(function( request, response ) { + equal( request.term, "ja" ); + response( ["java", "javascript"] ); + }); +}); + +test( "source, update after init", function() { + expect( 2 ); + var element = $( "#autocomplete" ).autocomplete({ + source: [ "java", "javascript", "haskell" ] + }), + menu = element.autocomplete( "widget" ); + element.val( "ja" ).autocomplete( "search" ); + equal( menu.find( ".ui-menu-item" ).text(), "javajavascript" ); + element.autocomplete( "option", "source", [ "php", "asp" ] ); + element.val( "ph" ).autocomplete( "search" ); + equal( menu.find( ".ui-menu-item" ).text(), "php" ); +}); + +}( jQuery ) ); diff --git a/apps/it/static/js/ui/tests/unit/autocomplete/remote_object_array_labels.txt b/apps/it/static/js/ui/tests/unit/autocomplete/remote_object_array_labels.txt new file mode 100644 index 0000000..502496c --- /dev/null +++ b/apps/it/static/js/ui/tests/unit/autocomplete/remote_object_array_labels.txt @@ -0,0 +1 @@ +[{"label":"java"},{"label":"javascript"}]
\ No newline at end of file diff --git a/apps/it/static/js/ui/tests/unit/autocomplete/remote_object_array_values.txt b/apps/it/static/js/ui/tests/unit/autocomplete/remote_object_array_values.txt new file mode 100644 index 0000000..029cbb9 --- /dev/null +++ b/apps/it/static/js/ui/tests/unit/autocomplete/remote_object_array_values.txt @@ -0,0 +1 @@ +[{"value":"java"},{"value":"javascript"}]
\ No newline at end of file diff --git a/apps/it/static/js/ui/tests/unit/autocomplete/remote_string_array.txt b/apps/it/static/js/ui/tests/unit/autocomplete/remote_string_array.txt new file mode 100644 index 0000000..3b24c8e --- /dev/null +++ b/apps/it/static/js/ui/tests/unit/autocomplete/remote_string_array.txt @@ -0,0 +1 @@ +["java", "javascript"]
\ No newline at end of file diff --git a/apps/it/static/js/ui/tests/unit/button/all.html b/apps/it/static/js/ui/tests/unit/button/all.html new file mode 100644 index 0000000..871006f --- /dev/null +++ b/apps/it/static/js/ui/tests/unit/button/all.html @@ -0,0 +1,30 @@ +<!doctype html> +<html lang="en"> +<head> + <meta charset="utf-8"> + <title>jQuery UI Button Test Suite</title> + + <script src="../../../jquery-1.10.2.js"></script> + + <link rel="stylesheet" href="../../../external/qunit.css"> + <link rel="stylesheet" href="../qunit-composite.css"> + <script src="../../../external/qunit.js"></script> + <script src="../qunit-composite.js"></script> + <script src="../subsuite.js"></script> + + <script> + testAllVersions( "button" ); + </script> +</head> +<body> + +<h1 id="qunit-header">jQuery UI Button Test Suite</h1> +<h2 id="qunit-banner"></h2> +<div id="qunit-testrunner-toolbar"></div> +<h2 id="qunit-userAgent"></h2> +<ol id="qunit-tests"></ol> +<div id="qunit-fixture"> + +</div> +</body> +</html> diff --git a/apps/it/static/js/ui/tests/unit/button/button.html b/apps/it/static/js/ui/tests/unit/button/button.html new file mode 100644 index 0000000..223581e --- /dev/null +++ b/apps/it/static/js/ui/tests/unit/button/button.html @@ -0,0 +1,78 @@ +<!doctype html> +<html lang="en"> +<head> + <meta charset="utf-8"> + <title>jQuery UI Button Test Suite</title> + + <script src="../../jquery.js"></script> + <link rel="stylesheet" href="../../../external/qunit.css"> + <script src="../../../external/qunit.js"></script> + <script src="../../jquery.simulate.js"></script> + <script src="../testsuite.js"></script> + <script> + TestHelpers.loadResources({ + css: [ "ui.core", "ui.button" ], + js: [ + "ui/jquery.ui.core.js", + "ui/jquery.ui.widget.js", + "ui/jquery.ui.button.js" + ] + }); + </script> + + <script src="button_common.js"></script> + <script src="button_core.js"></script> + <script src="button_events.js"></script> + <script src="button_methods.js"></script> + <script src="button_options.js"></script> + + <script src="../swarminject.js"></script> +</head> +<body> + +<h1 id="qunit-header">jQuery UI Button Test Suite</h1> +<h2 id="qunit-banner"></h2> +<div id="qunit-testrunner-toolbar"></div> +<h2 id="qunit-userAgent"></h2> +<ol id="qunit-tests"></ol> +<div id="qunit-fixture"> + +<div><button id="button" class="foo">Label</button></div> + +<div id="radio0" style="margin-top: 2em;"> + <input type="radio" id="radio01" name="radio" checked="checked"><label for="radio01">Choice 1</label> + <input type="radio" id="radio02" name="radio"><label for="radio02">Choice 2</label> + <input type="radio" id="radio03" name="radio"><label for="radio03">Choice 3</label> +</div> +<form> + <div id="radio1" style="margin-top: 2em;"> + <input type="radio" id="radio11" name="radio"><label for="radio11">Choice 1</label> + <input type="radio" id="radio12" name="radio" checked="checked"><label for="radio12">Choice 2</label> + <input type="radio" id="radio13" name="radio"><label for="radio13">Choice 3</label> + </div> +</form> +<form> + <div id="radio2" style="margin-top: 2em;"> + <input type="radio" id="radio21" name="radio"><label for="radio21">Choice 1</label> + <input type="radio" id="radio22" name="radio"><label for="radio22">Choice 2</label> + <input type="radio" id="radio23" name="radio" checked="checked"><label for="radio23">Choice 3</label> + </div> +</form> +<form> + <div id="radio3"> + <input type="radio" id="radio31" name="data['Page']['parse']"><label for="radio31">Choice 1</label> + <input type="radio" id="radio32" name="data['Page']['parse']" checked="checked"><label for="radio32">Choice 2</label> + <input type="radio" id="radio33" name="data['Page']['parse']"><label for="radio33">Choice 3</label> + </div> +</form> + +<input type="checkbox" id="check"><label for="check">Toggle</label> +<input type="checkbox" id="check2"><label for="check2">Checkbox</label> + +<div><input id="submit" type="submit" value="Label"></div> + +<button id="button1">Button</button> + +</div> +</body> +</html> diff --git a/apps/it/static/js/ui/tests/unit/button/button_common.js b/apps/it/static/js/ui/tests/unit/button/button_common.js new file mode 100644 index 0000000..ef22d30 --- /dev/null +++ b/apps/it/static/js/ui/tests/unit/button/button_common.js @@ -0,0 +1,14 @@ +TestHelpers.commonWidgetTests( "button", { + defaults: { + disabled: null, + icons: { + primary: null, + secondary: null + }, + label: null, + text: true, + + // callbacks + create: null + } +}); diff --git a/apps/it/static/js/ui/tests/unit/button/button_core.js b/apps/it/static/js/ui/tests/unit/button/button_core.js new file mode 100644 index 0000000..41623e0 --- /dev/null +++ b/apps/it/static/js/ui/tests/unit/button/button_core.js @@ -0,0 +1,212 @@ +/* + * button_core.js + */ + + +(function($) { + +module("button: core"); + +test("checkbox", function() { + expect( 4 ); + var input = $("#check"), + label = $("label[for=check]"); + ok( input.is(":visible") ); + ok( label.is(":not(.ui-button)") ); + input.button(); + ok( input.is(".ui-helper-hidden-accessible") ); + ok( label.is(".ui-button") ); +}); + +test("radios", function() { + expect( 4 ); + var inputs = $("#radio0 input"), + labels = $("#radio0 label"); + ok( inputs.is(":visible") ); + ok( labels.is(":not(.ui-button)") ); + inputs.button(); + ok( inputs.is(".ui-helper-hidden-accessible") ); + ok( labels.is(".ui-button") ); +}); + +function assert(noForm, form1, form2) { + ok( $("#radio0 .ui-button" + noForm).is(".ui-state-active") ); + ok( $("#radio1 .ui-button" + form1).is(".ui-state-active") ); + ok( $("#radio2 .ui-button" + form2).is(".ui-state-active") ); +} + +test("radio groups", function() { + expect( 12 ); + $("input[type=radio]").button(); + assert(":eq(0)", ":eq(1)", ":eq(2)"); + + // click outside of forms + $("#radio0 .ui-button:eq(1)").click(); + assert(":eq(1)", ":eq(1)", ":eq(2)"); + + // click in first form + $("#radio1 .ui-button:eq(0)").click(); + assert(":eq(1)", ":eq(0)", ":eq(2)"); + + // click in second form + $("#radio2 .ui-button:eq(0)").click(); + assert(":eq(1)", ":eq(0)", ":eq(0)"); +}); + +test("input type submit, don't create child elements", function() { + expect( 2 ); + var input = $("#submit"); + deepEqual( input.children().length, 0 ); + input.button(); + deepEqual( input.children().length, 0 ); +}); + +test("buttonset", function() { + expect( 6 ); + var set = $("#radio1").buttonset(); + ok( set.is(".ui-buttonset") ); + deepEqual( set.children(".ui-button").length, 3 ); + deepEqual( set.children("input[type=radio].ui-helper-hidden-accessible").length, 3 ); + ok( set.children("label:eq(0)").is(".ui-button.ui-corner-left:not(.ui-corner-all)") ); + ok( set.children("label:eq(1)").is(".ui-button:not(.ui-corner-all)") ); + ok( set.children("label:eq(2)").is(".ui-button.ui-corner-right:not(.ui-corner-all)") ); +}); + +test("buttonset (rtl)", function() { + expect( 6 ); + var set, + parent = $("#radio1").parent(); + // Set to rtl + parent.attr("dir", "rtl"); + + set = $("#radio1").buttonset(); + ok( set.is(".ui-buttonset") ); + deepEqual( set.children(".ui-button").length, 3 ); + deepEqual( set.children("input[type=radio].ui-helper-hidden-accessible").length, 3 ); + ok( set.children("label:eq(0)").is(".ui-button.ui-corner-right:not(.ui-corner-all)") ); + ok( set.children("label:eq(1)").is(".ui-button:not(.ui-corner-all)") ); + ok( set.children("label:eq(2)").is(".ui-button.ui-corner-left:not(.ui-corner-all)") ); +}); + +// TODO: simulated click events don't behave like real click events in IE +// remove this when simulate properly simulates this +// see http://yuilibrary.com/projects/yui2/ticket/2528826 fore more info +if ( !$.ui.ie || ( document.documentMode && document.documentMode > 8 ) ) { + asyncTest( "ensure checked and aria after single click on checkbox label button, see #5518", function() { + expect( 3 ); + + $("#check2").button().change( function() { + var lbl = $( this ).button("widget"); + ok( this.checked, "checked ok" ); + ok( lbl.attr("aria-pressed") === "true", "aria ok" ); + ok( lbl.hasClass("ui-state-active"), "ui-state-active ok" ); + }); + + // support: Opera + // Opera doesn't trigger a change event when this is done synchronously. + // This seems to be a side effect of another test, but until that can be + // tracked down, this delay will have to do. + setTimeout(function() { + $("#check2").button("widget").simulate("click"); + start(); + }, 1 ); + }); +} + +test( "#7092 - button creation that requires a matching label does not find label in all cases", function() { + expect( 5 ); + var group = $( "<span><label for='t7092a'></label><input type='checkbox' id='t7092a'></span>" ); + group.find( "input[type=checkbox]" ).button(); + ok( group.find( "label" ).is( ".ui-button" ) ); + + group = $( "<input type='checkbox' id='t7092b'><label for='t7092b'></label>" ); + group.filter( "input[type=checkbox]" ).button(); + ok( group.filter( "label" ).is( ".ui-button" ) ); + + group = $( "<span><input type='checkbox' id='t7092c'></span><label for='t7092c'></label>" ); + group.find( "input[type=checkbox]" ).button(); + ok( group.filter( "label" ).is( ".ui-button" ) ); + + group = $( "<span><input type='checkbox' id='t7092d'></span><span><label for='t7092d'></label></span>" ); + group.find( "input[type=checkbox]" ).button(); + ok( group.find( "label" ).is( ".ui-button" ) ); + + group = $( "<input type='checkbox' id='t7092e'><span><label for='t7092e'></label></span>" ); + group.filter( "input[type=checkbox]" ).button(); + ok( group.find( "label" ).is( ".ui-button" ) ); +}); + +test( "#5946 - buttonset should ignore buttons that are not :visible", function() { + expect( 2 ); + $( "#radio01" ).next().addBack().hide(); + var set = $( "#radio0" ).buttonset({ items: "input[type=radio]:visible" }); + ok( set.find( "label:eq(0)" ).is( ":not(.ui-button):not(.ui-corner-left)" ) ); + ok( set.find( "label:eq(1)" ).is( ".ui-button.ui-corner-left" ) ); +}); + +test( "#6262 - buttonset not applying ui-corner to invisible elements", function() { + expect( 3 ); + $( "#radio0" ).hide(); + var set = $( "#radio0" ).buttonset(); + ok( set.find( "label:eq(0)" ).is( ".ui-button.ui-corner-left" ) ); + ok( set.find( "label:eq(1)" ).is( ".ui-button" ) ); + ok( set.find( "label:eq(2)" ).is( ".ui-button.ui-corner-right" ) ); +}); + +asyncTest( "Resetting a button's form should refresh the visual state of the button widget to match.", function() { + expect( 2 ); + var form = $( "<form>" + + "<button></button>" + + "<label for='c1'></label><input id='c1' type='checkbox' checked>" + + "</form>" ), + button = form.find( "button" ).button(), + checkbox = form.find( "input[type=checkbox]" ).button(); + + checkbox.prop( "checked", false ).button( "refresh" ); + ok( !checkbox.button( "widget" ).hasClass( "ui-state-active" ) ); + + form.get( 0 ).reset(); + + // #9213: If a button has been removed, refresh should not be called on it when + // its corresponding form is reset. + button.remove(); + + setTimeout(function() { + ok( checkbox.button( "widget" ).hasClass( "ui-state-active" )); + start(); + }, 1 ); +}); + +asyncTest( "#6711 Checkbox/Radiobutton do not Show Focused State when using Keyboard Navigation", function() { + expect( 2 ); + var check = $( "#check" ).button(), + label = $( "label[for='check']" ); + ok( !label.is( ".ui-state-focus" ) ); + check.focus(); + setTimeout(function() { + ok( label.is( ".ui-state-focus" ) ); + start(); + }); +}); + +test( "#7534 - Button label selector works for ids with \":\"", function() { + expect( 1 ); + var group = $( "<span><input type='checkbox' id='check:7534'><label for='check:7534'>Label</label></span>" ); + group.find( "input" ).button(); + ok( group.find( "label" ).is( ".ui-button" ), "Found an id with a :" ); +}); + +asyncTest( "#9169 - Disabled button maintains ui-state-focus", function() { + expect( 2 ); + var element = $( "#button1" ).button(); + element[ 0 ].focus(); + setTimeout(function() { + ok( element.hasClass( "ui-state-focus" ), "button has ui-state-focus" ); + element.button( "disable" ); + ok( !element.hasClass( "ui-state-focus" ), + "button does not have ui-state-focus when disabled" ); + start(); + }); +}); + +})(jQuery); diff --git a/apps/it/static/js/ui/tests/unit/button/button_events.js b/apps/it/static/js/ui/tests/unit/button/button_events.js new file mode 100644 index 0000000..2fd0383 --- /dev/null +++ b/apps/it/static/js/ui/tests/unit/button/button_events.js @@ -0,0 +1,36 @@ +/* + * button_events.js + */ +(function($) { + +module("button: events"); + +test("buttonset works with single-quote named elements (#7505)", function() { + expect( 1 ); + $("#radio3").buttonset(); + $("#radio33").click( function(){ + ok( true, "button clicks work with single-quote named elements" ); + }).click(); +}); + +asyncTest( "when button loses focus, ensure active state is removed (#8559)", function() { + expect( 1 ); + + var element = $( "#button" ).button(); + + element.one( "keypress", function() { + element.one( "blur", function() { + ok( !element.is(".ui-state-active"), "button loses active state appropriately" ); + start(); + }).blur(); + }); + + element.focus(); + setTimeout(function() { + element + .simulate( "keydown", { keyCode: $.ui.keyCode.ENTER } ) + .simulate( "keypress", { keyCode: $.ui.keyCode.ENTER } ); + }); +}); + +})(jQuery); diff --git a/apps/it/static/js/ui/tests/unit/button/button_methods.js b/apps/it/static/js/ui/tests/unit/button/button_methods.js new file mode 100644 index 0000000..467938f --- /dev/null +++ b/apps/it/static/js/ui/tests/unit/button/button_methods.js @@ -0,0 +1,52 @@ +/* + * button_methods.js + */ +(function($) { + + +module("button: methods"); + +test("destroy", function() { + expect( 1 ); + domEqual( "#button", function() { + $( "#button" ).button().button( "destroy" ); + }); +}); + +test( "refresh: Ensure disabled state is preserved correctly.", function() { + expect( 8 ); + + var element = $( "<a href='#'></a>" ); + element.button({ disabled: true }).button( "refresh" ); + ok( element.button( "option", "disabled" ), "Anchor button should remain disabled after refresh" ); //See #8237 + + element = $( "<div></div>" ); + element.button({ disabled: true }).button( "refresh" ); + ok( element.button( "option", "disabled" ), "<div> buttons should remain disabled after refresh" ); + + element = $( "<button></button>" ); + element.button( { disabled: true} ).button( "refresh" ); + ok( element.button( "option", "disabled" ), "<button> should remain disabled after refresh"); + + element = $( "<input type='checkbox'>" ); + element.button( { disabled: true} ).button( "refresh" ); + ok( element.button( "option", "disabled" ), "Checkboxes should remain disabled after refresh"); + + element = $( "<input type='radio'>" ); + element.button( { disabled: true} ).button( "refresh" ); + ok( element.button( "option", "disabled" ), "Radio buttons should remain disabled after refresh"); + + element = $( "<button></button>" ); + element.button( { disabled: true} ).prop( "disabled", false ).button( "refresh" ); + ok( !element.button( "option", "disabled" ), "Changing a <button>'s disabled property should update the state after refresh."); //See #8828 + + element = $( "<input type='checkbox'>" ); + element.button( { disabled: true} ).prop( "disabled", false ).button( "refresh" ); + ok( !element.button( "option", "disabled" ), "Changing a checkbox's disabled property should update the state after refresh."); + + element = $( "<input type='radio'>" ); + element.button( { disabled: true} ).prop( "disabled", false ).button( "refresh" ); + ok( !element.button( "option", "disabled" ), "Changing a radio button's disabled property should update the state after refresh."); +}); + +})(jQuery); diff --git a/apps/it/static/js/ui/tests/unit/button/button_options.js b/apps/it/static/js/ui/tests/unit/button/button_options.js new file mode 100644 index 0000000..e1931a5 --- /dev/null +++ b/apps/it/static/js/ui/tests/unit/button/button_options.js @@ -0,0 +1,117 @@ +/* + * button_options.js + */ +(function($) { + +module("button: options"); + +test("disabled, explicit value", function() { + expect( 4 ); + $("#radio01").button({ disabled: false }); + deepEqual(false, $("#radio01").button("option", "disabled"), + "disabled option set to false"); + deepEqual(false, $("#radio01").prop("disabled"), "element is disabled"); + + $("#radio02").button({ disabled: true }); + deepEqual(true, $("#radio02").button("option", "disabled"), + "disabled option set to true"); + deepEqual(true, $("#radio02").prop("disabled"), "element is not disabled"); +}); + +test("disabled, null", function() { + expect( 4 ); + $("#radio01").button({ disabled: null }); + deepEqual(false, $("#radio01").button("option", "disabled"), + "disabled option set to false"); + deepEqual(false, $("#radio01").prop("disabled"), "element is disabled"); + + $("#radio02").prop("disabled", true).button({ disabled: null }); + deepEqual(true, $("#radio02").button("option", "disabled"), + "disabled option set to true"); + deepEqual(true, $("#radio02").prop("disabled"), "element is not disabled"); +}); + +test("text false without icon", function() { + expect( 1 ); + $("#button").button({ + text: false + }); + ok( $("#button").is(".ui-button-text-only:not(.ui-button-icon-only)") ); + + $("#button").button("destroy"); +}); + +test("text false with icon", function() { + expect( 1 ); + $("#button").button({ + text: false, + icons: { + primary: "iconclass" + } + }); + ok( $("#button").is(".ui-button-icon-only:not(.ui-button-text):has(span.ui-icon.iconclass)") ); + + $("#button").button("destroy"); +}); + +test("label, default", function() { + expect( 2 ); + $("#button").button(); + deepEqual( $("#button").text(), "Label" ); + deepEqual( $( "#button").button( "option", "label" ), "Label" ); + + $("#button").button("destroy"); +}); + +test("label", function() { + expect( 2 ); + $("#button").button({ + label: "xxx" + }); + deepEqual( $("#button").text(), "xxx" ); + deepEqual( $("#button").button( "option", "label" ), "xxx" ); + + $("#button").button("destroy"); +}); + +test("label default with input type submit", function() { + expect( 2 ); + deepEqual( $("#submit").button().val(), "Label" ); + deepEqual( $("#submit").button( "option", "label" ), "Label" ); +}); + +test("label with input type submit", function() { + expect( 2 ); + var label = $("#submit").button({ + label: "xxx" + }).val(); + deepEqual( label, "xxx" ); + deepEqual( $("#submit").button( "option", "label" ), "xxx" ); +}); + +test("icons", function() { + expect( 1 ); + $("#button").button({ + text: false, + icons: { + primary: "iconclass", + secondary: "iconclass2" + } + }); + ok( $("#button").is(":has(span.ui-icon.ui-button-icon-primary.iconclass):has(span.ui-icon.ui-button-icon-secondary.iconclass2)") ); + + $("#button").button("destroy"); +}); + +test( "#5295 - button does not remove hoverstate if disabled" , function() { + expect( 1 ); + var btn = $("#button").button(); + btn.hover( function() { + btn.button( "disable" ); + }); + btn.trigger( "mouseenter" ); + btn.trigger( "mouseleave" ); + ok( !btn.is( ".ui-state-hover") ); +}); + +})(jQuery); diff --git a/apps/it/static/js/ui/tests/unit/core/all.html b/apps/it/static/js/ui/tests/unit/core/all.html new file mode 100644 index 0000000..b24a472 --- /dev/null +++ b/apps/it/static/js/ui/tests/unit/core/all.html @@ -0,0 +1,30 @@ +<!doctype html> +<html lang="en"> +<head> + <meta charset="utf-8"> + <title>jQuery UI Core Test Suite</title> + + <script src="../../../jquery-1.10.2.js"></script> + + <link rel="stylesheet" href="../../../external/qunit.css"> + <link rel="stylesheet" href="../qunit-composite.css"> + <script src="../../../external/qunit.js"></script> + <script src="../qunit-composite.js"></script> + <script src="../subsuite.js"></script> + + <script> + testAllVersions( "core" ); + </script> +</head> +<body> + +<h1 id="qunit-header">jQuery UI Core Test Suite</h1> +<h2 id="qunit-banner"></h2> +<div id="qunit-testrunner-toolbar"></div> +<h2 id="qunit-userAgent"></h2> +<ol id="qunit-tests"></ol> +<div id="qunit-fixture"> + +</div> +</body> +</html> diff --git a/apps/it/static/js/ui/tests/unit/core/core.html b/apps/it/static/js/ui/tests/unit/core/core.html new file mode 100644 index 0000000..41c8db8 --- /dev/null +++ b/apps/it/static/js/ui/tests/unit/core/core.html @@ -0,0 +1,135 @@ +<!doctype html> +<html lang="en"> +<head> + <meta charset="utf-8"> + <title>jQuery UI Core Test Suite</title> + + <script src="../../jquery.js"></script> + <link rel="stylesheet" href="../../../external/qunit.css"> + <script src="../../../external/qunit.js"></script> + <script src="../../jquery.simulate.js"></script> + <script src="../testsuite.js"></script> + <script> + TestHelpers.loadResources({ + js: [ "ui/jquery.ui.core.js" ] + }); + </script> + + <script src="core.js"></script> + <script src="selector.js"></script> + + <script src="../swarminject.js"></script> + <style> + .zindex { + z-index: 100; + } + .absolute { + position: absolute; + } + </style> +</head> +<body> + +<h1 id="qunit-header">jQuery UI Core Test Suite</h1> +<h2 id="qunit-banner"></h2> +<div id="qunit-testrunner-toolbar"></div> +<h2 id="qunit-userAgent"></h2> +<ol id="qunit-tests"></ol> +<div id="qunit-fixture"> + +<img src="../images/jqueryui_32x32.png" usemap="#mymap" width="10" height="10" alt=""> +<map name="mymap"> + <area shape="rect" coords="1,1,2,2" href="foo.html" id="areaCoordsHref" alt=""> + <area href="foo.html" id="areaNoCoordsHref" alt=""> +</map> +<map name="mymap2"> + <area shape="rect" coords="1,1,2,2" href="foo.html" id="areaNoImg" alt=""> +</map> + +<form id="formNoTabindex"></form> +<form id="formTabindex" tabindex="1"></form> + +<div> + <input id="visibleAncestor-inputTypeNone"> + <input type="text" id="visibleAncestor-inputTypeText"> + <input type="checkbox" id="visibleAncestor-inputTypeCheckbox"> + <input type="radio" id="visibleAncestor-inputTypeRadio"> + <input type="button" id="visibleAncestor-inputTypeButton" value="visibleAncestor-inputTypeButton"> + <input type="hidden" id="visibleAncestor-inputTypeHidden"> + <button id="visibleAncestor-button">x</button> + <select id="visibleAncestor-select"> + <option>option</option> + </select> + <textarea id="visibleAncestor-textarea">x</textarea> + <object id="visibleAncestor-object" codebase="about:blank">xxx</object> + <a href="#" id="visibleAncestor-anchorWithHref">anchor</a> + <a id="visibleAncestor-anchorWithoutHref">anchor</a> + <span id="visibleAncestor-span">x</span> + <div id="visibleAncestor-div">x</div> + <span id="visibleAncestor-spanWithTabindex" tabindex="1">x</span> + <div id="visibleAncestor-divWithNegativeTabindex" tabindex="-1">x</div> +</div> + +<div> + <input id="disabledElement-inputTypeNone" disabled="disabled"> + <input type="text" id="disabledElement-inputTypeText" disabled="disabled"> + <input type="checkbox" id="disabledElement-inputTypeCheckbox" disabled="disabled"> + <input type="radio" id="disabledElement-inputTypeRadio" disabled="disabled"> + <input type="button" id="disabledElement-inputTypeButton" disabled="disabled" value="disabledElement-inputTypeButton"> + <input type="hidden" id="disabledElement-inputTypeHidden" disabled="disabled"> + <button id="disabledElement-button" disabled="disabled"></button> + <select id="disabledElement-select" disabled="disabled"></select> + <textarea id="disabledElement-textarea" disabled="disabled"></textarea> +</div> + +<div> + <div id="displayNoneAncestor" style="display: none;"> + <input id="displayNoneAncestor-input"> + <span tabindex="1" id="displayNoneAncestor-span">.</span> + </div> + + <div id="visibilityHiddenAncestor" style="visibility: hidden;"> + <input id="visibilityHiddenAncestor-input"> + <span tabindex="1" id="visibilityHiddenAncestor-span">.</span> + </div> + + <span tabindex="1" id="displayNone-span" style="display: none;">.</span> + <span tabindex="1" id="visibilityHidden-span" style="visibility: hidden;">.</span> + + <input id="displayNone-input" style="display: none;"> + <input id="visibilityHidden-input" style="visibility: hidden;"> +</div> + +<div> + <input id="inputTabindex0" tabindex="0"> + <input id="inputTabindex10" tabindex="10"> + <input id="inputTabindex-1" tabindex="-1"> + <input id="inputTabindex-50" tabindex="-50"> + + <span id="spanTabindex0" tabindex="0">.</span> + <span id="spanTabindex10" tabindex="10">.</span> + <span id="spanTabindex-1" tabindex="-1">.</span> + <span id="spanTabindex-50" tabindex="-50">.</span> +</div> + +<div style="width: 0; height: 0;"> + <input id="dimensionlessParent"> + <input id="dimensionlessParent-dimensionless" style="height: 0; width: 0;"> +</div> + +<div id="zIndex100" style="z-index: 100; position: absolute"> + <div id="zIndexAutoWithParent">.</div> +</div> +<div id="zIndex100ViaCSS" class="zindex"> + <div id="zIndexAutoWithParentViaCSS">.</div> +</div> +<div id="zIndex100ViaCSSPositioned" class="zindex absolute"> + <div id="zIndexAutoWithParentViaCSSPositioned">.</div> +</div> +<div id="zIndexAutoNoParent"></div> + +<div id="dimensions" style="float: left; height: 50px; width: 100px; margin: 1px 12px 11px 2px; border-style: solid; border-width: 3px 14px 13px 4px; padding: 5px 16px 15px 6px;"></div> + +</div> +</body> +</html> diff --git a/apps/it/static/js/ui/tests/unit/core/core.js b/apps/it/static/js/ui/tests/unit/core/core.js new file mode 100644 index 0000000..ab19aad --- /dev/null +++ b/apps/it/static/js/ui/tests/unit/core/core.js @@ -0,0 +1,191 @@ +(function( $ ) { + +module( "core - jQuery extensions" ); + +TestHelpers.testJshint( "core" ); + +asyncTest( "focus - original functionality", function() { + expect( 1 ); + $( "#inputTabindex0" ) + .one( "focus", function() { + ok( true, "event triggered" ); + start(); + }) + .focus(); +}); + +asyncTest( "focus", function() { + expect( 2 ); + + // support: IE 8 + // IE sometimes gets confused about what's focused if we don't explicitly + // focus a different element first + $( "body" ).focus(); + + $( "#inputTabindex0" ) + .one( "focus", function() { + ok( true, "event triggered" ); + start(); + }) + .focus( 500, function() { + ok( true, "callback triggered" ); + }); +}); + +test( "zIndex", function() { + expect( 7 ); + var el = $( "#zIndexAutoWithParent" ), + parent = el.parent(); + equal( el.zIndex(), 100, "zIndex traverses up to find value" ); + equal( parent.zIndex(200 ), parent, "zIndex setter is chainable" ); + equal( el.zIndex(), 200, "zIndex setter changed zIndex" ); + + el = $( "#zIndexAutoWithParentViaCSS" ); + equal( el.zIndex(), 0, "zIndex traverses up to find CSS value, not found because not positioned" ); + + el = $( "#zIndexAutoWithParentViaCSSPositioned" ); + equal( el.zIndex(), 100, "zIndex traverses up to find CSS value" ); + el.parent().zIndex( 200 ); + equal( el.zIndex(), 200, "zIndex setter changed zIndex, overriding CSS" ); + + equal( $( "#zIndexAutoNoParent" ).zIndex(), 0, "zIndex never explicitly set in hierarchy" ); +}); + +test( "innerWidth - getter", function() { + expect( 2 ); + var el = $( "#dimensions" ); + + equal( el.innerWidth(), 122, "getter passthru" ); + el.hide(); + equal( el.innerWidth(), 122, "getter passthru when hidden" ); +}); + +test( "innerWidth - setter", function() { + expect( 2 ); + var el = $( "#dimensions" ); + + el.innerWidth( 120 ); + equal( el.width(), 98, "width set properly" ); + el.hide(); + el.innerWidth( 100 ); + equal( el.width(), 78, "width set properly when hidden" ); +}); + +test( "innerHeight - getter", function() { + expect( 2 ); + var el = $( "#dimensions" ); + + equal( el.innerHeight(), 70, "getter passthru" ); + el.hide(); + equal( el.innerHeight(), 70, "getter passthru when hidden" ); +}); + +test( "innerHeight - setter", function() { + expect( 2 ); + var el = $( "#dimensions" ); + + el.innerHeight( 60 ); + equal( el.height(), 40, "height set properly" ); + el.hide(); + el.innerHeight( 50 ); + equal( el.height(), 30, "height set properly when hidden" ); +}); + +test( "outerWidth - getter", function() { + expect( 2 ); + var el = $( "#dimensions" ); + + equal( el.outerWidth(), 140, "getter passthru" ); + el.hide(); + equal( el.outerWidth(), 140, "getter passthru when hidden" ); +}); + +test( "outerWidth - setter", function() { + expect( 2 ); + var el = $( "#dimensions" ); + + el.outerWidth( 130 ); + equal( el.width(), 90, "width set properly" ); + el.hide(); + el.outerWidth( 120 ); + equal( el.width(), 80, "width set properly when hidden" ); +}); + +test( "outerWidth(true) - getter", function() { + expect( 2 ); + var el = $( "#dimensions" ); + + equal( el.outerWidth(true), 154, "getter passthru w/ margin" ); + el.hide(); + equal( el.outerWidth(true), 154, "getter passthru w/ margin when hidden" ); +}); + +test( "outerWidth(true) - setter", function() { + expect( 2 ); + var el = $( "#dimensions" ); + + el.outerWidth( 130, true ); + equal( el.width(), 76, "width set properly" ); + el.hide(); + el.outerWidth( 120, true ); + equal( el.width(), 66, "width set properly when hidden" ); +}); + +test( "outerHeight - getter", function() { + expect( 2 ); + var el = $( "#dimensions" ); + + equal( el.outerHeight(), 86, "getter passthru" ); + el.hide(); + equal( el.outerHeight(), 86, "getter passthru when hidden" ); +}); + +test( "outerHeight - setter", function() { + expect( 2 ); + var el = $( "#dimensions" ); + + el.outerHeight( 80 ); + equal( el.height(), 44, "height set properly" ); + el.hide(); + el.outerHeight( 70 ); + equal( el.height(), 34, "height set properly when hidden" ); +}); + +test( "outerHeight(true) - getter", function() { + expect( 2 ); + var el = $( "#dimensions" ); + + equal( el.outerHeight(true), 98, "getter passthru w/ margin" ); + el.hide(); + equal( el.outerHeight(true), 98, "getter passthru w/ margin when hidden" ); +}); + +test( "outerHeight(true) - setter", function() { + expect( 2 ); + var el = $( "#dimensions" ); + + el.outerHeight( 90, true ); + equal( el.height(), 42, "height set properly" ); + el.hide(); + el.outerHeight( 80, true ); + equal( el.height(), 32, "height set properly when hidden" ); +}); + +test( "uniqueId / removeUniqueId", function() { + expect( 3 ); + var el = $( "img" ).eq( 0 ); + + // support: jQuery <1.6.2 + // support: IE <8 + // We should use strictEqual( id, undefined ) when dropping jQuery 1.6.1 support (or IE6/7) + ok( !el.attr( "id" ), "element has no initial id" ); + el.uniqueId(); + ok( /ui-id-\d+$/.test( el.attr( "id" ) ), "element has generated id" ); + el.removeUniqueId(); + // support: jQuery <1.6.2 + // support: IE <8 + // see above + ok( !el.attr( "id" ), "unique id has been removed from element" ); +}); + +})( jQuery ); diff --git a/apps/it/static/js/ui/tests/unit/core/selector.js b/apps/it/static/js/ui/tests/unit/core/selector.js new file mode 100644 index 0000000..f86526b --- /dev/null +++ b/apps/it/static/js/ui/tests/unit/core/selector.js @@ -0,0 +1,251 @@ +/* + * selector unit tests + */ +(function($) { + +module("core - selectors"); + +function isFocusable(selector, msg) { + QUnit.push($(selector).is(":focusable"), null, null, msg + " - selector " + selector + " is focusable"); +} + +function isNotFocusable(selector, msg) { + QUnit.push($(selector).length && !$(selector).is(":focusable"), null, null, msg + " - selector " + selector + " is not focusable"); +} + +function isTabbable(selector, msg) { + QUnit.push($(selector).is(":tabbable"), null, null, msg + " - selector " + selector + " is tabbable"); +} + +function isNotTabbable(selector, msg) { + QUnit.push($(selector).length && !$(selector).is(":tabbable"), null, null, msg + " - selector " + selector + " is not tabbable"); +} + +test("data", function() { + expect(15); + + var el; + function shouldHaveData(msg) { + ok(el.is(":data(test)"), msg); + } + function shouldNotHaveData(msg) { + ok(!el.is(":data(test)"), msg); + } + + el = $("<div>"); + shouldNotHaveData("data never set"); + + el = $("<div>").data("test", null); + shouldNotHaveData("data is null"); + + el = $("<div>").data("test", true); + shouldHaveData("data set to true"); + + el = $("<div>").data("test", false); + shouldNotHaveData("data set to false"); + + el = $("<div>").data("test", 0); + shouldNotHaveData("data set to 0"); + + el = $("<div>").data("test", 1); + shouldHaveData("data set to 1"); + + el = $("<div>").data("test", ""); + shouldNotHaveData("data set to empty string"); + + el = $("<div>").data("test", "foo"); + shouldHaveData("data set to string"); + + el = $("<div>").data("test", []); + shouldHaveData("data set to empty array"); + + el = $("<div>").data("test", [1]); + shouldHaveData("data set to array"); + + el = $("<div>").data("test", {}); + shouldHaveData("data set to empty object"); + + el = $("<div>").data("test", {foo: "bar"}); + shouldHaveData("data set to object"); + + el = $("<div>").data("test", new Date()); + shouldHaveData("data set to date"); + + el = $("<div>").data("test", /test/); + shouldHaveData("data set to regexp"); + + el = $("<div>").data("test", function() {}); + shouldHaveData("data set to function"); +}); + +test("focusable - visible, enabled elements", function() { + expect(18); + + isNotFocusable("#formNoTabindex", "form"); + isFocusable("#formTabindex", "form with tabindex"); + isFocusable("#visibleAncestor-inputTypeNone", "input, no type"); + isFocusable("#visibleAncestor-inputTypeText", "input, type text"); + isFocusable("#visibleAncestor-inputTypeCheckbox", "input, type checkbox"); + isFocusable("#visibleAncestor-inputTypeRadio", "input, type radio"); + isFocusable("#visibleAncestor-inputTypeButton", "input, type button"); + isNotFocusable("#visibleAncestor-inputTypeHidden", "input, type hidden"); + isFocusable("#visibleAncestor-button", "button"); + isFocusable("#visibleAncestor-select", "select"); + isFocusable("#visibleAncestor-textarea", "textarea"); + isFocusable("#visibleAncestor-object", "object"); + isFocusable("#visibleAncestor-anchorWithHref", "anchor with href"); + isNotFocusable("#visibleAncestor-anchorWithoutHref", "anchor without href"); + isNotFocusable("#visibleAncestor-span", "span"); + isNotFocusable("#visibleAncestor-div", "div"); + isFocusable("#visibleAncestor-spanWithTabindex", "span with tabindex"); + isFocusable("#visibleAncestor-divWithNegativeTabindex", "div with tabindex"); +}); + +test("focusable - disabled elements", function() { + expect(9); + + isNotFocusable("#disabledElement-inputTypeNone", "input, no type"); + isNotFocusable("#disabledElement-inputTypeText", "input, type text"); + isNotFocusable("#disabledElement-inputTypeCheckbox", "input, type checkbox"); + isNotFocusable("#disabledElement-inputTypeRadio", "input, type radio"); + isNotFocusable("#disabledElement-inputTypeButton", "input, type button"); + isNotFocusable("#disabledElement-inputTypeHidden", "input, type hidden"); + isNotFocusable("#disabledElement-button", "button"); + isNotFocusable("#disabledElement-select", "select"); + isNotFocusable("#disabledElement-textarea", "textarea"); +}); + +test("focusable - hidden styles", function() { + expect(8); + + isNotFocusable("#displayNoneAncestor-input", "input, display: none parent"); + isNotFocusable("#displayNoneAncestor-span", "span with tabindex, display: none parent"); + + isNotFocusable("#visibilityHiddenAncestor-input", "input, visibility: hidden parent"); + isNotFocusable("#visibilityHiddenAncestor-span", "span with tabindex, visibility: hidden parent"); + + isNotFocusable("#displayNone-input", "input, display: none"); + isNotFocusable("#visibilityHidden-input", "input, visibility: hidden"); + + isNotFocusable("#displayNone-span", "span with tabindex, display: none"); + isNotFocusable("#visibilityHidden-span", "span with tabindex, visibility: hidden"); +}); + +test("focusable - natively focusable with various tabindex", function() { + expect(4); + + isFocusable("#inputTabindex0", "input, tabindex 0"); + isFocusable("#inputTabindex10", "input, tabindex 10"); + isFocusable("#inputTabindex-1", "input, tabindex -1"); + isFocusable("#inputTabindex-50", "input, tabindex -50"); +}); + +test("focusable - not natively focusable with various tabindex", function() { + expect(4); + + isFocusable("#spanTabindex0", "span, tabindex 0"); + isFocusable("#spanTabindex10", "span, tabindex 10"); + isFocusable("#spanTabindex-1", "span, tabindex -1"); + isFocusable("#spanTabindex-50", "span, tabindex -50"); +}); + +test("focusable - area elements", function() { + expect( 3 ); + + isFocusable("#areaCoordsHref", "coords and href"); + isFocusable("#areaNoCoordsHref", "href but no coords"); + isNotFocusable("#areaNoImg", "not associated with an image"); +}); + +test( "focusable - dimensionless parent with overflow", function() { + expect( 1 ); + + isFocusable( "#dimensionlessParent", "input" ); +}); + +test("tabbable - visible, enabled elements", function() { + expect(18); + + isNotTabbable("#formNoTabindex", "form"); + isTabbable("#formTabindex", "form with tabindex"); + isTabbable("#visibleAncestor-inputTypeNone", "input, no type"); + isTabbable("#visibleAncestor-inputTypeText", "input, type text"); + isTabbable("#visibleAncestor-inputTypeCheckbox", "input, type checkbox"); + isTabbable("#visibleAncestor-inputTypeRadio", "input, type radio"); + isTabbable("#visibleAncestor-inputTypeButton", "input, type button"); + isNotTabbable("#visibleAncestor-inputTypeHidden", "input, type hidden"); + isTabbable("#visibleAncestor-button", "button"); + isTabbable("#visibleAncestor-select", "select"); + isTabbable("#visibleAncestor-textarea", "textarea"); + isTabbable("#visibleAncestor-object", "object"); + isTabbable("#visibleAncestor-anchorWithHref", "anchor with href"); + isNotTabbable("#visibleAncestor-anchorWithoutHref", "anchor without href"); + isNotTabbable("#visibleAncestor-span", "span"); + isNotTabbable("#visibleAncestor-div", "div"); + isTabbable("#visibleAncestor-spanWithTabindex", "span with tabindex"); + isNotTabbable("#visibleAncestor-divWithNegativeTabindex", "div with tabindex"); +}); + +test("tabbable - disabled elements", function() { + expect(9); + + isNotTabbable("#disabledElement-inputTypeNone", "input, no type"); + isNotTabbable("#disabledElement-inputTypeText", "input, type text"); + isNotTabbable("#disabledElement-inputTypeCheckbox", "input, type checkbox"); + isNotTabbable("#disabledElement-inputTypeRadio", "input, type radio"); + isNotTabbable("#disabledElement-inputTypeButton", "input, type button"); + isNotTabbable("#disabledElement-inputTypeHidden", "input, type hidden"); + isNotTabbable("#disabledElement-button", "button"); + isNotTabbable("#disabledElement-select", "select"); + isNotTabbable("#disabledElement-textarea", "textarea"); +}); + +test("tabbable - hidden styles", function() { + expect(8); + + isNotTabbable("#displayNoneAncestor-input", "input, display: none parent"); + isNotTabbable("#displayNoneAncestor-span", "span with tabindex, display: none parent"); + + isNotTabbable("#visibilityHiddenAncestor-input", "input, visibility: hidden parent"); + isNotTabbable("#visibilityHiddenAncestor-span", "span with tabindex, visibility: hidden parent"); + + isNotTabbable("#displayNone-input", "input, display: none"); + isNotTabbable("#visibilityHidden-input", "input, visibility: hidden"); + + isNotTabbable("#displayNone-span", "span with tabindex, display: none"); + isNotTabbable("#visibilityHidden-span", "span with tabindex, visibility: hidden"); +}); + +test("tabbable - natively tabbable with various tabindex", function() { + expect(4); + + isTabbable("#inputTabindex0", "input, tabindex 0"); + isTabbable("#inputTabindex10", "input, tabindex 10"); + isNotTabbable("#inputTabindex-1", "input, tabindex -1"); + isNotTabbable("#inputTabindex-50", "input, tabindex -50"); +}); + +test("tabbable - not natively tabbable with various tabindex", function() { + expect(4); + + isTabbable("#spanTabindex0", "span, tabindex 0"); + isTabbable("#spanTabindex10", "span, tabindex 10"); + isNotTabbable("#spanTabindex-1", "span, tabindex -1"); + isNotTabbable("#spanTabindex-50", "span, tabindex -50"); +}); + +test("tabbable - area elements", function() { + expect( 3 ); + + isTabbable("#areaCoordsHref", "coords and href"); + isTabbable("#areaNoCoordsHref", "href but no coords"); + isNotTabbable("#areaNoImg", "not associated with an image"); +}); + +test( "tabbable - dimensionless parent with overflow", function() { + expect( 1 ); + + isTabbable( "#dimensionlessParent", "input" ); +}); + +})(jQuery); diff --git a/apps/it/static/js/ui/tests/unit/datepicker/all.html b/apps/it/static/js/ui/tests/unit/datepicker/all.html new file mode 100644 index 0000000..a5b68ed --- /dev/null +++ b/apps/it/static/js/ui/tests/unit/datepicker/all.html @@ -0,0 +1,30 @@ +<!doctype html> +<html lang="en"> +<head> + <meta charset="utf-8"> + <title>jQuery UI Datepicker Test Suite</title> + + <script src="../../../jquery-1.10.2.js"></script> + + <link rel="stylesheet" href="../../../external/qunit.css"> + <link rel="stylesheet" href="../qunit-composite.css"> + <script src="../../../external/qunit.js"></script> + <script src="../qunit-composite.js"></script> + <script src="../subsuite.js"></script> + + <script> + testAllVersions( "datepicker" ); + </script> +</head> +<body> + +<h1 id="qunit-header">jQuery UI Datepicker Test Suite</h1> +<h2 id="qunit-banner"></h2> +<div id="qunit-testrunner-toolbar"></div> +<h2 id="qunit-userAgent"></h2> +<ol id="qunit-tests"></ol> +<div id="qunit-fixture"> + +</div> +</body> +</html> diff --git a/apps/it/static/js/ui/tests/unit/datepicker/datepicker.html b/apps/it/static/js/ui/tests/unit/datepicker/datepicker.html new file mode 100644 index 0000000..8ef2de5 --- /dev/null +++ b/apps/it/static/js/ui/tests/unit/datepicker/datepicker.html @@ -0,0 +1,48 @@ +<!doctype html> +<html lang="en"> +<head> + <meta charset="utf-8"> + <title>jQuery UI Datepicker Test Suite</title> + + <script src="../../jquery.js"></script> + <link rel="stylesheet" href="../../../external/qunit.css"> + <script src="../../../external/qunit.js"></script> + <script src="../../jquery.simulate.js"></script> + <script src="../testsuite.js"></script> + <script> + TestHelpers.loadResources({ + css: [ "ui.core", "ui.datepicker" ], + js: [ + "ui/jquery.ui.core.js", + "ui/jquery.ui.datepicker.js", + "ui/i18n/jquery.ui.datepicker-fr.js", + "ui/i18n/jquery.ui.datepicker-he.js", + "ui/i18n/jquery.ui.datepicker-zh-CN.js" + ] + }); + </script> + + <script src="datepicker_common.js"></script> + <script src="datepicker_core.js"></script> + <script src="datepicker_events.js"></script> + <script src="datepicker_methods.js"></script> + <script src="datepicker_options.js"></script> + <script src="datepicker_test_helpers.js"></script> + + <script src="../swarminject.js"></script> +</head> +<body> + +<h1 id="qunit-header">jQuery UI Datepicker Test Suite</h1> +<h2 id="qunit-banner"></h2> +<div id="qunit-testrunner-toolbar"></div> +<h2 id="qunit-userAgent"></h2> +<ol id="qunit-tests"></ol> +<div id="qunit-fixture"> + +<div><input type="text" id="inp"><input type="text" id="alt"><div id="inl"></div></div> +<p><input type="text" id="inp2"></p> + +</div> +</body> +</html> diff --git a/apps/it/static/js/ui/tests/unit/datepicker/datepicker_common.js b/apps/it/static/js/ui/tests/unit/datepicker/datepicker_common.js new file mode 100644 index 0000000..1eecc85 --- /dev/null +++ b/apps/it/static/js/ui/tests/unit/datepicker/datepicker_common.js @@ -0,0 +1,7 @@ +/* +TestHelpers.commonWidgetTests( "datepicker", { + defaults: { + disabled: false + } +}); +*/ diff --git a/apps/it/static/js/ui/tests/unit/datepicker/datepicker_core.js b/apps/it/static/js/ui/tests/unit/datepicker/datepicker_core.js new file mode 100644 index 0000000..0bc5723 --- /dev/null +++ b/apps/it/static/js/ui/tests/unit/datepicker/datepicker_core.js @@ -0,0 +1,524 @@ +/* + * datepicker_core.js + */ + +(function($) { + +module("datepicker: core"); + +TestHelpers.testJshint( "datepicker" ); + +test("initialization - Reinitialization after body had been emptied.", function() { + expect( 1 ); + var bodyContent = $("body").children(), inp = $("#inp"); + $("#inp").datepicker(); + $("body").empty().append(inp); + $("#inp").datepicker(); + ok( $("#"+$.datepicker._mainDivId).length===1, "Datepicker container added" ); + $("body").empty().append(bodyContent); // Returning to initial state for later tests +}); + +test( "widget method - empty collection", function() { + expect( 1 ); + $( "#nonExist" ).datepicker(); // should create nothing + ok( !$( "#ui-datepicker-div" ).length, "Non init on empty collection" ); +}); + +test("widget method", function() { + expect( 1 ); + var actual = $("#inp").datepicker().datepicker("widget")[0]; + deepEqual($("body > #ui-datepicker-div:last-child")[0], actual); +}); + +asyncTest( "baseStructure", function() { + expect( 58 ); + var header, title, table, thead, week, panel, inl, child, + inp = TestHelpers.datepicker.initNewInput(), + dp = $( "#ui-datepicker-div" ); + + function step1() { + TestHelpers.datepicker.onFocus( inp, function() { + ok( dp.is( ":visible" ), "Structure - datepicker visible" ); + ok( !dp.is( ".ui-datepicker-rtl" ), "Structure - not right-to-left" ); + ok( !dp.is( ".ui-datepicker-multi" ), "Structure - not multi-month" ); + equal( dp.children().length, 2, "Structure - child count" ); + + header = dp.children( ":first" ); + ok( header.is( "div.ui-datepicker-header" ), "Structure - header division" ); + equal( header.children().length, 3, "Structure - header child count" ); + ok( header.children( ":first" ).is( "a.ui-datepicker-prev" ) && header.children( ":first" ).html() !== "", "Structure - prev link" ); + ok( header.children( ":eq(1)" ).is( "a.ui-datepicker-next" ) && header.children( ":eq(1)" ).html() !== "", "Structure - next link" ); + + title = header.children( ":last" ); + ok( title.is( "div.ui-datepicker-title" ) && title.html() !== "","Structure - title division" ); + equal( title.children().length, 2, "Structure - title child count" ); + ok( title.children( ":first" ).is( "span.ui-datepicker-month" ) && title.children( ":first" ).text() !== "", "Structure - month text" ); + ok( title.children( ":last" ).is( "span.ui-datepicker-year" ) && title.children( ":last" ).text() !== "", "Structure - year text" ); + + table = dp.children( ":eq(1)" ); + ok( table.is( "table.ui-datepicker-calendar" ), "Structure - month table" ); + ok( table.children( ":first" ).is( "thead" ), "Structure - month table thead" ); + + thead = table.children( ":first" ).children( ":first" ); + ok( thead.is( "tr" ), "Structure - month table title row" ); + equal( thead.find( "th" ).length, 7, "Structure - month table title cells" ); + ok( table.children( ":eq(1)" ).is( "tbody" ), "Structure - month table body" ); + ok( table.children( ":eq(1)" ).children( "tr" ).length >= 4, "Structure - month table week count" ); + + week = table.children( ":eq(1)" ).children( ":first" ); + ok( week.is( "tr" ), "Structure - month table week row" ); + equal( week.children().length, 7, "Structure - week child count" ); + ok( week.children( ":first" ).is( "td.ui-datepicker-week-end" ), "Structure - month table first day cell" ); + ok( week.children( ":last" ).is( "td.ui-datepicker-week-end" ), "Structure - month table second day cell" ); + + inp.datepicker( "hide" ).datepicker( "destroy" ); + step2(); + }); + } + + function step2() { + // Editable month/year and button panel + inp = TestHelpers.datepicker.initNewInput({ + changeMonth: true, + changeYear: true, + showButtonPanel: true + }); + TestHelpers.datepicker.onFocus( inp, function() { + title = dp.find( "div.ui-datepicker-title" ); + ok( title.children( ":first" ).is( "select.ui-datepicker-month" ), "Structure - month selector" ); + ok( title.children( ":last" ).is( "select.ui-datepicker-year" ), "Structure - year selector" ); + + panel = dp.children( ":last" ); + ok( panel.is( "div.ui-datepicker-buttonpane" ), "Structure - button panel division" ); + equal( panel.children().length, 2, "Structure - button panel child count" ); + ok( panel.children( ":first" ).is( "button.ui-datepicker-current" ), "Structure - today button" ); + ok( panel.children( ":last" ).is( "button.ui-datepicker-close" ), "Structure - close button" ); + + inp.datepicker( "hide" ).datepicker( "destroy" ); + step3(); + }); + } + + function step3() { + // Multi-month 2 + inp = TestHelpers.datepicker.initNewInput({ numberOfMonths: 2 }); + TestHelpers.datepicker.onFocus( inp, function() { + ok( dp.is( ".ui-datepicker-multi" ), "Structure multi [2] - multi-month" ); + equal( dp.children().length, 3, "Structure multi [2] - child count" ); + + child = dp.children( ":first" ); + ok( child.is( "div.ui-datepicker-group" ) && child.is( "div.ui-datepicker-group-first" ), "Structure multi [2] - first month division" ); + + child = dp.children( ":eq(1)" ); + ok( child.is( "div.ui-datepicker-group" ) && child.is( "div.ui-datepicker-group-last" ), "Structure multi [2] - second month division" ); + + child = dp.children( ":eq(2)" ); + ok( child.is( "div.ui-datepicker-row-break" ), "Structure multi [2] - row break" ); + ok( dp.is( ".ui-datepicker-multi-2" ), "Structure multi [2] - multi-2" ); + + inp.datepicker( "hide" ).datepicker( "destroy" ); + step4(); + }); + } + + function step4() { + // Multi-month 3 + inp = TestHelpers.datepicker.initNewInput({ numberOfMonths: 3 }); + TestHelpers.datepicker.onFocus( inp, function() { + ok( dp.is( ".ui-datepicker-multi-3" ), "Structure multi [3] - multi-3" ); + ok( !dp.is( ".ui-datepicker-multi-2" ), "Structure multi [3] - Trac #6704" ); + + inp.datepicker( "hide" ).datepicker( "destroy" ); + step5(); + }); + } + + function step5() { + // Multi-month [2, 2] + inp = TestHelpers.datepicker.initNewInput({ numberOfMonths: [ 2, 2 ] }); + TestHelpers.datepicker.onFocus( inp, function() { + ok( dp.is( ".ui-datepicker-multi" ), "Structure multi - multi-month" ); + equal( dp.children().length, 6, "Structure multi [2,2] - child count" ); + + child = dp.children( ":first" ); + ok( child.is( "div.ui-datepicker-group" ) && child.is( "div.ui-datepicker-group-first" ), "Structure multi [2,2] - first month division" ); + + child = dp.children( ":eq(1)" ); + ok( child.is( "div.ui-datepicker-group" ) && child.is( "div.ui-datepicker-group-last" ), "Structure multi [2,2] - second month division" ); + + child = dp.children( ":eq(2)" ); + ok( child.is( "div.ui-datepicker-row-break" ), "Structure multi [2,2] - row break" ); + + child = dp.children( ":eq(3)" ); + ok( child.is( "div.ui-datepicker-group" ) && child.is( "div.ui-datepicker-group-first" ), "Structure multi [2,2] - third month division" ); + + child = dp.children( ":eq(4)" ); + ok( child.is( "div.ui-datepicker-group" ) && child.is( "div.ui-datepicker-group-last" ), "Structure multi [2,2] - fourth month division" ); + + child = dp.children( ":eq(5)" ); + ok( child.is( "div.ui-datepicker-row-break" ), "Structure multi [2,2] - row break" ); + + inp.datepicker( "hide" ).datepicker( "destroy" ); + + // Inline + inl = TestHelpers.datepicker.init( "#inl" ); + dp = inl.children(); + + ok( dp.is( ".ui-datepicker-inline" ), "Structure inline - main div" ); + ok( !dp.is( ".ui-datepicker-rtl" ), "Structure inline - not right-to-left" ); + ok( !dp.is( ".ui-datepicker-multi" ), "Structure inline - not multi-month" ); + equal( dp.children().length, 2, "Structure inline - child count" ); + + header = dp.children( ":first" ); + ok( header.is( "div.ui-datepicker-header" ), "Structure inline - header division" ); + equal( header.children().length, 3, "Structure inline - header child count" ); + + table = dp.children( ":eq(1)" ); + ok( table.is( "table.ui-datepicker-calendar" ), "Structure inline - month table" ); + ok( table.children( ":first" ).is( "thead" ), "Structure inline - month table thead" ); + ok( table.children( ":eq(1)" ).is( "tbody" ), "Structure inline - month table body" ); + + inl.datepicker( "destroy" ); + + // Inline multi-month + inl = TestHelpers.datepicker.init( "#inl", { numberOfMonths: 2 } ); + dp = inl.children(); + + ok( dp.is( ".ui-datepicker-inline" ) && dp.is( ".ui-datepicker-multi" ), "Structure inline multi - main div" ); + equal( dp.children().length, 3, "Structure inline multi - child count" ); + + child = dp.children( ":first" ); + ok( child.is( "div.ui-datepicker-group" ) && child.is( "div.ui-datepicker-group-first" ), "Structure inline multi - first month division" ); + + child = dp.children( ":eq(1)" ); + ok( child.is( "div.ui-datepicker-group" ) && child.is( "div.ui-datepicker-group-last" ), "Structure inline multi - second month division" ); + + child = dp.children( ":eq(2)" ); + ok( child.is( "div.ui-datepicker-row-break" ), "Structure inline multi - row break" ); + + inl.datepicker( "destroy" ); + start(); + }); + } + + step1(); +}); + +asyncTest( "customStructure", function() { + expect( 20 ); + var header, panel, title, thead, + inp = TestHelpers.datepicker.initNewInput( $.datepicker.regional.he ), + dp = $( "#ui-datepicker-div" ); + + function step1() { + inp.datepicker( "option", "showButtonPanel", true ); + + TestHelpers.datepicker.onFocus( inp, function() { + ok( dp.is( ".ui-datepicker-rtl" ), "Structure RTL - right-to-left" ); + + header = dp.children( ":first" ); + ok( header.is( "div.ui-datepicker-header" ), "Structure RTL - header division" ); + equal( header.children().length, 3, "Structure RTL - header child count" ); + ok( header.children( ":first" ).is( "a.ui-datepicker-next" ), "Structure RTL - prev link" ); + ok( header.children( ":eq(1)" ).is( "a.ui-datepicker-prev" ), "Structure RTL - next link" ); + + panel = dp.children( ":last" ); + ok( panel.is( "div.ui-datepicker-buttonpane" ), "Structure RTL - button division" ); + equal( panel.children().length, 2, "Structure RTL - button panel child count" ); + ok( panel.children( ":first" ).is( "button.ui-datepicker-close" ), "Structure RTL - close button" ); + ok( panel.children( ":last" ).is( "button.ui-datepicker-current" ), "Structure RTL - today button" ); + + inp.datepicker( "hide" ).datepicker( "destroy" ); + step2(); + }); + } + + // Hide prev/next + function step2() { + inp = TestHelpers.datepicker.initNewInput({ + hideIfNoPrevNext: true, + minDate: new Date( 2008, 2 - 1, 4 ), + maxDate: new Date( 2008, 2 - 1, 14 ) + }); + inp.val( "02/10/2008" ); + + TestHelpers.datepicker.onFocus( inp, function() { + header = dp.children( ":first" ); + ok( header.is( "div.ui-datepicker-header" ), "Structure hide prev/next - header division" ); + equal( header.children().length, 1, "Structure hide prev/next - links child count" ); + ok( header.children( ":first" ).is( "div.ui-datepicker-title" ), "Structure hide prev/next - title division" ); + + inp.datepicker( "hide" ).datepicker( "destroy" ); + step3(); + }); + } + + // Changeable Month with read-only year + function step3() { + inp = TestHelpers.datepicker.initNewInput({ changeMonth: true }); + + TestHelpers.datepicker.onFocus( inp, function() { + title = dp.children( ":first" ).children( ":last" ); + equal( title.children().length, 2, "Structure changeable month - title child count" ); + ok( title.children( ":first" ).is( "select.ui-datepicker-month" ), "Structure changeable month - month selector" ); + ok( title.children( ":last" ).is( "span.ui-datepicker-year" ), "Structure changeable month - read-only year" ); + + inp.datepicker( "hide" ).datepicker( "destroy" ); + step4(); + }); + } + + // Changeable year with read-only month + function step4() { + inp = TestHelpers.datepicker.initNewInput({ changeYear: true }); + + TestHelpers.datepicker.onFocus( inp, function() { + title = dp.children( ":first" ).children( ":last" ); + equal( title.children().length, 2, "Structure changeable year - title child count" ); + ok( title.children( ":first" ).is( "span.ui-datepicker-month" ), "Structure changeable year - read-only month" ); + ok( title.children( ":last" ).is( "select.ui-datepicker-year" ), "Structure changeable year - year selector" ); + + inp.datepicker( "hide" ).datepicker( "destroy" ); + step5(); + }); + } + + // Read-only first day of week + function step5() { + inp = TestHelpers.datepicker.initNewInput({ changeFirstDay: false }); + + TestHelpers.datepicker.onFocus( inp, function() { + thead = dp.find( ".ui-datepicker-calendar thead tr" ); + equal( thead.children().length, 7, "Structure read-only first day - thead child count" ); + equal( thead.find( "a" ).length, 0, "Structure read-only first day - thead links count" ); + + inp.datepicker( "hide" ).datepicker( "destroy" ); + start(); + }); + } + + // TODO: figure out why this setTimeout is needed in IE, + // it only is necessary when the previous baseStructure tests runs first + // Support: IE + setTimeout( step1 ); +}); + +test("keystrokes", function() { + expect( 26 ); + var inp = TestHelpers.datepicker.init("#inp"), + date = new Date(); + inp.val("").datepicker("show"). + simulate("keydown", {keyCode: $.ui.keyCode.ENTER}); + TestHelpers.datepicker.equalsDate(inp.datepicker("getDate"), date, "Keystroke enter"); + inp.val("02/04/2008").datepicker("show"). + simulate("keydown", {keyCode: $.ui.keyCode.ENTER}); + TestHelpers.datepicker.equalsDate(inp.datepicker("getDate"), new Date(2008, 2 - 1, 4), + "Keystroke enter - preset"); + inp.val("02/04/2008").datepicker("show"). + simulate("keydown", {ctrlKey: true, keyCode: $.ui.keyCode.HOME}). + simulate("keydown", {keyCode: $.ui.keyCode.ENTER}); + TestHelpers.datepicker.equalsDate(inp.datepicker("getDate"), date, "Keystroke ctrl+home"); + inp.val("02/04/2008").datepicker("show"). + simulate("keydown", {ctrlKey: true, keyCode: $.ui.keyCode.END}); + ok(inp.datepicker("getDate") == null, "Keystroke ctrl+end"); + inp.val("").datepicker("show"). + simulate("keydown", {keyCode: $.ui.keyCode.ESCAPE}); + ok(inp.datepicker("getDate") == null, "Keystroke esc"); + inp.val("02/04/2008").datepicker("show"). + simulate("keydown", {keyCode: $.ui.keyCode.ESCAPE}); + TestHelpers.datepicker.equalsDate(inp.datepicker("getDate"), new Date(2008, 2 - 1, 4), + "Keystroke esc - preset"); + inp.val("02/04/2008").datepicker("show"). + simulate("keydown", {ctrlKey: true, keyCode: $.ui.keyCode.PAGE_UP}). + simulate("keydown", {keyCode: $.ui.keyCode.ESCAPE}); + TestHelpers.datepicker.equalsDate(inp.datepicker("getDate"), new Date(2008, 2 - 1, 4), + "Keystroke esc - abandoned"); + // Moving by day or week + inp.val("").datepicker("show"). + simulate("keydown", {ctrlKey: true, keyCode: $.ui.keyCode.LEFT}). + simulate("keydown", {keyCode: $.ui.keyCode.ENTER}); + date.setDate(date.getDate() - 1); + TestHelpers.datepicker.equalsDate(inp.datepicker("getDate"), date, "Keystroke ctrl+left"); + inp.val("").datepicker("show"). + simulate("keydown", {keyCode: $.ui.keyCode.LEFT}). + simulate("keydown", {keyCode: $.ui.keyCode.ENTER}); + date.setDate(date.getDate() + 1); + TestHelpers.datepicker.equalsDate(inp.datepicker("getDate"), date, "Keystroke left"); + inp.val("").datepicker("show"). + simulate("keydown", {ctrlKey: true, keyCode: $.ui.keyCode.RIGHT}). + simulate("keydown", {keyCode: $.ui.keyCode.ENTER}); + date.setDate(date.getDate() + 1); + TestHelpers.datepicker.equalsDate(inp.datepicker("getDate"), date, "Keystroke ctrl+right"); + inp.val("").datepicker("show"). + simulate("keydown", {keyCode: $.ui.keyCode.RIGHT}). + simulate("keydown", {keyCode: $.ui.keyCode.ENTER}); + date.setDate(date.getDate() - 1); + TestHelpers.datepicker.equalsDate(inp.datepicker("getDate"), date, "Keystroke right"); + inp.val("").datepicker("show"). + simulate("keydown", {ctrlKey: true, keyCode: $.ui.keyCode.UP}). + simulate("keydown", {keyCode: $.ui.keyCode.ENTER}); + date.setDate(date.getDate() - 7); + TestHelpers.datepicker.equalsDate(inp.datepicker("getDate"), date, "Keystroke ctrl+up"); + inp.val("").datepicker("show"). + simulate("keydown", {keyCode: $.ui.keyCode.UP}). + simulate("keydown", {keyCode: $.ui.keyCode.ENTER}); + date.setDate(date.getDate() + 7); + TestHelpers.datepicker.equalsDate(inp.datepicker("getDate"), date, "Keystroke up"); + inp.val("").datepicker("show"). + simulate("keydown", {ctrlKey: true, keyCode: $.ui.keyCode.DOWN}). + simulate("keydown", {keyCode: $.ui.keyCode.ENTER}); + date.setDate(date.getDate() + 7); + TestHelpers.datepicker.equalsDate(inp.datepicker("getDate"), date, "Keystroke ctrl+down"); + inp.val("").datepicker("show"). + simulate("keydown", {keyCode: $.ui.keyCode.DOWN}). + simulate("keydown", {keyCode: $.ui.keyCode.ENTER}); + date.setDate(date.getDate() - 7); + TestHelpers.datepicker.equalsDate(inp.datepicker("getDate"), date, "Keystroke down"); + // Moving by month or year + inp.val("02/04/2008").datepicker("show"). + simulate("keydown", {keyCode: $.ui.keyCode.PAGE_UP}). + simulate("keydown", {keyCode: $.ui.keyCode.ENTER}); + TestHelpers.datepicker.equalsDate(inp.datepicker("getDate"), new Date(2008, 1 - 1, 4), + "Keystroke pgup"); + inp.val("02/04/2008").datepicker("show"). + simulate("keydown", {keyCode: $.ui.keyCode.PAGE_DOWN}). + simulate("keydown", {keyCode: $.ui.keyCode.ENTER}); + TestHelpers.datepicker.equalsDate(inp.datepicker("getDate"), new Date(2008, 3 - 1, 4), + "Keystroke pgdn"); + inp.val("02/04/2008").datepicker("show"). + simulate("keydown", {ctrlKey: true, keyCode: $.ui.keyCode.PAGE_UP}). + simulate("keydown", {keyCode: $.ui.keyCode.ENTER}); + TestHelpers.datepicker.equalsDate(inp.datepicker("getDate"), new Date(2007, 2 - 1, 4), + "Keystroke ctrl+pgup"); + inp.val("02/04/2008").datepicker("show"). + simulate("keydown", {ctrlKey: true, keyCode: $.ui.keyCode.PAGE_DOWN}). + simulate("keydown", {keyCode: $.ui.keyCode.ENTER}); + TestHelpers.datepicker.equalsDate(inp.datepicker("getDate"), new Date(2009, 2 - 1, 4), + "Keystroke ctrl+pgdn"); + // Check for moving to short months + inp.val("03/31/2008").datepicker("show"). + simulate("keydown", {keyCode: $.ui.keyCode.PAGE_UP}). + simulate("keydown", {keyCode: $.ui.keyCode.ENTER}); + TestHelpers.datepicker.equalsDate(inp.datepicker("getDate"), new Date(2008, 2 - 1, 29), + "Keystroke pgup - Feb"); + inp.val("01/30/2008").datepicker("show"). + simulate("keydown", {keyCode: $.ui.keyCode.PAGE_DOWN}). + simulate("keydown", {keyCode: $.ui.keyCode.ENTER}); + TestHelpers.datepicker.equalsDate(inp.datepicker("getDate"), new Date(2008, 2 - 1, 29), + "Keystroke pgdn - Feb"); + inp.val("02/29/2008").datepicker("show"). + simulate("keydown", {ctrlKey: true, keyCode: $.ui.keyCode.PAGE_UP}). + simulate("keydown", {keyCode: $.ui.keyCode.ENTER}); + TestHelpers.datepicker.equalsDate(inp.datepicker("getDate"), new Date(2007, 2 - 1, 28), + "Keystroke ctrl+pgup - Feb"); + inp.val("02/29/2008").datepicker("show"). + simulate("keydown", {ctrlKey: true, keyCode: $.ui.keyCode.PAGE_DOWN}). + simulate("keydown", {keyCode: $.ui.keyCode.ENTER}); + TestHelpers.datepicker.equalsDate(inp.datepicker("getDate"), new Date(2009, 2 - 1, 28), + "Keystroke ctrl+pgdn - Feb"); + // Goto current + inp.datepicker("option", {gotoCurrent: true}). + datepicker("hide").val("02/04/2008").datepicker("show"). + simulate("keydown", {keyCode: $.ui.keyCode.PAGE_DOWN}). + simulate("keydown", {ctrlKey: true, keyCode: $.ui.keyCode.HOME}). + simulate("keydown", {keyCode: $.ui.keyCode.ENTER}); + TestHelpers.datepicker.equalsDate(inp.datepicker("getDate"), new Date(2008, 2 - 1, 4), + "Keystroke ctrl+home"); + // Change steps + inp.datepicker("option", {stepMonths: 2, gotoCurrent: false}). + datepicker("hide").val("02/04/2008").datepicker("show"). + simulate("keydown", {keyCode: $.ui.keyCode.PAGE_UP}). + simulate("keydown", {keyCode: $.ui.keyCode.ENTER}); + TestHelpers.datepicker.equalsDate(inp.datepicker("getDate"), new Date(2007, 12 - 1, 4), + "Keystroke pgup step 2"); + inp.val("02/04/2008").datepicker("show"). + simulate("keydown", {keyCode: $.ui.keyCode.PAGE_DOWN}). + simulate("keydown", {keyCode: $.ui.keyCode.ENTER}); + TestHelpers.datepicker.equalsDate(inp.datepicker("getDate"), new Date(2008, 4 - 1, 4), + "Keystroke pgdn step 2"); +}); + +test("mouse", function() { + expect( 15 ); + var inl, + inp = TestHelpers.datepicker.init("#inp"), + dp = $("#ui-datepicker-div"), + date = new Date(); + inp.val("").datepicker("show"); + $(".ui-datepicker-calendar tbody a:contains(10)", dp).simulate("click", {}); + date.setDate(10); + TestHelpers.datepicker.equalsDate(inp.datepicker("getDate"), date, "Mouse click"); + inp.val("02/04/2008").datepicker("show"); + $(".ui-datepicker-calendar tbody a:contains(12)", dp).simulate("click", {}); + TestHelpers.datepicker.equalsDate(inp.datepicker("getDate"), new Date(2008, 2 - 1, 12), + "Mouse click - preset"); + inp.val("02/04/2008").datepicker("show"); + inp.val("").datepicker("show"); + $("button.ui-datepicker-close", dp).simulate("click", {}); + ok(inp.datepicker("getDate") == null, "Mouse click - close"); + inp.val("02/04/2008").datepicker("show"); + $("button.ui-datepicker-close", dp).simulate("click", {}); + TestHelpers.datepicker.equalsDate(inp.datepicker("getDate"), new Date(2008, 2 - 1, 4), + "Mouse click - close + preset"); + inp.val("02/04/2008").datepicker("show"); + $("a.ui-datepicker-prev", dp).simulate("click", {}); + $("button.ui-datepicker-close", dp).simulate("click", {}); + TestHelpers.datepicker.equalsDate(inp.datepicker("getDate"), new Date(2008, 2 - 1, 4), + "Mouse click - abandoned"); + // Current/previous/next + inp.val("02/04/2008").datepicker("option", {showButtonPanel: true}).datepicker("show"); + $(".ui-datepicker-current", dp).simulate("click", {}); + $(".ui-datepicker-calendar tbody a:contains(14)", dp).simulate("click", {}); + date.setDate(14); + TestHelpers.datepicker.equalsDate(inp.datepicker("getDate"), date, "Mouse click - current"); + inp.val("02/04/2008").datepicker("show"); + $(".ui-datepicker-prev", dp).simulate("click"); + $(".ui-datepicker-calendar tbody a:contains(16)", dp).simulate("click"); + TestHelpers.datepicker.equalsDate(inp.datepicker("getDate"), new Date(2008, 1 - 1, 16), + "Mouse click - previous"); + inp.val("02/04/2008").datepicker("show"); + $(".ui-datepicker-next", dp).simulate("click"); + $(".ui-datepicker-calendar tbody a:contains(18)", dp).simulate("click"); + TestHelpers.datepicker.equalsDate(inp.datepicker("getDate"), new Date(2008, 3 - 1, 18), + "Mouse click - next"); + // Previous/next with minimum/maximum + inp.datepicker("option", {minDate: new Date(2008, 2 - 1, 2), + maxDate: new Date(2008, 2 - 1, 26)}).val("02/04/2008").datepicker("show"); + $(".ui-datepicker-prev", dp).simulate("click"); + $(".ui-datepicker-calendar tbody a:contains(16)", dp).simulate("click"); + TestHelpers.datepicker.equalsDate(inp.datepicker("getDate"), new Date(2008, 2 - 1, 16), + "Mouse click - previous + min/max"); + inp.val("02/04/2008").datepicker("show"); + $(".ui-datepicker-next", dp).simulate("click"); + $(".ui-datepicker-calendar tbody a:contains(18)", dp).simulate("click"); + TestHelpers.datepicker.equalsDate(inp.datepicker("getDate"), new Date(2008, 2 - 1, 18), + "Mouse click - next + min/max"); + // Inline + inl = TestHelpers.datepicker.init("#inl"); + dp = $(".ui-datepicker-inline", inl); + date = new Date(); + inl.datepicker("setDate", date); + $(".ui-datepicker-calendar tbody a:contains(10)", dp).simulate("click", {}); + date.setDate(10); + TestHelpers.datepicker.equalsDate(inl.datepicker("getDate"), date, "Mouse click inline"); + inl.datepicker("option", {showButtonPanel: true}).datepicker("setDate", new Date(2008, 2 - 1, 4)); + $(".ui-datepicker-calendar tbody a:contains(12)", dp).simulate("click", {}); + TestHelpers.datepicker.equalsDate(inl.datepicker("getDate"), new Date(2008, 2 - 1, 12), "Mouse click inline - preset"); + inl.datepicker("option", {showButtonPanel: true}); + $(".ui-datepicker-current", dp).simulate("click", {}); + $(".ui-datepicker-calendar tbody a:contains(14)", dp).simulate("click", {}); + date.setDate(14); + TestHelpers.datepicker.equalsDate(inl.datepicker("getDate"), date, "Mouse click inline - current"); + inl.datepicker("setDate", new Date(2008, 2 - 1, 4)); + $(".ui-datepicker-prev", dp).simulate("click"); + $(".ui-datepicker-calendar tbody a:contains(16)", dp).simulate("click"); + TestHelpers.datepicker.equalsDate(inl.datepicker("getDate"), new Date(2008, 1 - 1, 16), + "Mouse click inline - previous"); + inl.datepicker("setDate", new Date(2008, 2 - 1, 4)); + $(".ui-datepicker-next", dp).simulate("click"); + $(".ui-datepicker-calendar tbody a:contains(18)", dp).simulate("click"); + TestHelpers.datepicker.equalsDate(inl.datepicker("getDate"), new Date(2008, 3 - 1, 18), + "Mouse click inline - next"); +}); + +})(jQuery); diff --git a/apps/it/static/js/ui/tests/unit/datepicker/datepicker_events.js b/apps/it/static/js/ui/tests/unit/datepicker/datepicker_events.js new file mode 100644 index 0000000..dfc42cc --- /dev/null +++ b/apps/it/static/js/ui/tests/unit/datepicker/datepicker_events.js @@ -0,0 +1,153 @@ +/* + * datepicker_events.js + */ +(function($) { + +module("datepicker: events"); + +var selectedThis = null, +selectedDate = null, +selectedInst = null; + +function callback(date, inst) { + selectedThis = this; + selectedDate = date; + selectedInst = inst; +} + +function callback2(year, month, inst) { + selectedThis = this; + selectedDate = year + "/" + month; + selectedInst = inst; +} + +test("events", function() { + expect( 26 ); + var dateStr, newMonthYear, inp2, + inp = TestHelpers.datepicker.init("#inp", {onSelect: callback}), + date = new Date(); + // onSelect + inp.val("").datepicker("show"). + simulate("keydown", {keyCode: $.ui.keyCode.ENTER}); + equal(selectedThis, inp[0], "Callback selected this"); + equal(selectedInst, $.data(inp[0], TestHelpers.datepicker.PROP_NAME), "Callback selected inst"); + equal(selectedDate, $.datepicker.formatDate("mm/dd/yy", date), + "Callback selected date"); + inp.val("").datepicker("show"). + simulate("keydown", {ctrlKey: true, keyCode: $.ui.keyCode.DOWN}). + simulate("keydown", {keyCode: $.ui.keyCode.ENTER}); + date.setDate(date.getDate() + 7); + equal(selectedDate, $.datepicker.formatDate("mm/dd/yy", date), + "Callback selected date - ctrl+down"); + inp.val("").datepicker("show"). + simulate("keydown", {keyCode: $.ui.keyCode.ESCAPE}); + equal(selectedDate, $.datepicker.formatDate("mm/dd/yy", date), + "Callback selected date - esc"); + dateStr = "02/04/2008"; + inp.val(dateStr).datepicker("show"). + simulate("keydown", {keyCode: $.ui.keyCode.ENTER}); + equal(dateStr, selectedDate, + "onSelect is called after enter keydown"); + // onChangeMonthYear + inp.datepicker("option", {onChangeMonthYear: callback2, onSelect: null}). + val("").datepicker("show"); + newMonthYear = function(date) { + return date.getFullYear() + "/" + (date.getMonth() + 1); + }; + date = new Date(); + date.setDate(1); + inp.simulate("keydown", {keyCode: $.ui.keyCode.PAGE_UP}); + date.setMonth(date.getMonth() - 1); + equal(selectedThis, inp[0], "Callback change month/year this"); + equal(selectedInst, $.data(inp[0], TestHelpers.datepicker.PROP_NAME), "Callback change month/year inst"); + equal(selectedDate, newMonthYear(date), + "Callback change month/year date - pgup"); + inp.simulate("keydown", {keyCode: $.ui.keyCode.PAGE_DOWN}); + date.setMonth(date.getMonth() + 1); + equal(selectedDate, newMonthYear(date), + "Callback change month/year date - pgdn"); + inp.simulate("keydown", {ctrlKey: true, keyCode: $.ui.keyCode.PAGE_UP}); + date.setFullYear(date.getFullYear() - 1); + equal(selectedDate, newMonthYear(date), + "Callback change month/year date - ctrl+pgup"); + inp.simulate("keydown", {ctrlKey: true, keyCode: $.ui.keyCode.HOME}); + date.setFullYear(date.getFullYear() + 1); + equal(selectedDate, newMonthYear(date), + "Callback change month/year date - ctrl+home"); + inp.simulate("keydown", {ctrlKey: true, keyCode: $.ui.keyCode.PAGE_DOWN}); + date.setFullYear(date.getFullYear() + 1); + equal(selectedDate, newMonthYear(date), + "Callback change month/year date - ctrl+pgdn"); + inp.datepicker("setDate", new Date(2007, 1 - 1, 26)); + equal(selectedDate, "2007/1", "Callback change month/year date - setDate"); + selectedDate = null; + inp.datepicker("setDate", new Date(2007, 1 - 1, 12)); + ok(selectedDate == null, "Callback change month/year date - setDate no change"); + // onChangeMonthYear step by 2 + inp.datepicker("option", {stepMonths: 2}). + datepicker("hide").val("").datepicker("show"). + simulate("keydown", {keyCode: $.ui.keyCode.PAGE_UP}); + date.setMonth(date.getMonth() - 14); + equal(selectedDate, newMonthYear(date), + "Callback change month/year by 2 date - pgup"); + inp.simulate("keydown", {ctrlKey: true, keyCode: $.ui.keyCode.PAGE_UP}); + date.setMonth(date.getMonth() - 12); + equal(selectedDate, newMonthYear(date), + "Callback change month/year by 2 date - ctrl+pgup"); + inp.simulate("keydown", {keyCode: $.ui.keyCode.PAGE_DOWN}); + date.setMonth(date.getMonth() + 2); + equal(selectedDate, newMonthYear(date), + "Callback change month/year by 2 date - pgdn"); + inp.simulate("keydown", {ctrlKey: true, keyCode: $.ui.keyCode.PAGE_DOWN}); + date.setMonth(date.getMonth() + 12); + equal(selectedDate, newMonthYear(date), + "Callback change month/year by 2 date - ctrl+pgdn"); + // onClose + inp.datepicker("option", {onClose: callback, onChangeMonthYear: null, stepMonths: 1}). + val("").datepicker("show"). + simulate("keydown", {keyCode: $.ui.keyCode.ESCAPE}); + equal(selectedThis, inp[0], "Callback close this"); + equal(selectedInst, $.data(inp[0], TestHelpers.datepicker.PROP_NAME), "Callback close inst"); + equal(selectedDate, "", "Callback close date - esc"); + inp.val("").datepicker("show"). + simulate("keydown", {keyCode: $.ui.keyCode.ENTER}); + equal(selectedDate, $.datepicker.formatDate("mm/dd/yy", new Date()), + "Callback close date - enter"); + inp.val("02/04/2008").datepicker("show"). + simulate("keydown", {keyCode: $.ui.keyCode.ESCAPE}); + equal(selectedDate, "02/04/2008", "Callback close date - preset"); + inp.val("02/04/2008").datepicker("show"). + simulate("keydown", {ctrlKey: true, keyCode: $.ui.keyCode.END}); + equal(selectedDate, "", "Callback close date - ctrl+end"); + + inp2 = TestHelpers.datepicker.init("#inp2"); + inp2.datepicker().datepicker("option", {onClose: callback}).datepicker("show"); + inp.datepicker("show"); + equal(selectedThis, inp2[0], "Callback close this"); +}); + +test("beforeShowDay-getDate", function() { + expect( 3 ); + var inp = TestHelpers.datepicker.init("#inp", {beforeShowDay: function() { inp.datepicker("getDate"); return [true, ""]; }}), + dp = $("#ui-datepicker-div"); + inp.val("01/01/2010").datepicker("show"); + // contains non-breaking space + equal($("div.ui-datepicker-title").text(), + // support: IE <9, jQuery <1.8 + // In IE7/8 with jQuery <1.8, encoded spaces behave in strange ways + $( "<span>January 2010</span>" ).text(), "Initial month"); + $("a.ui-datepicker-next", dp).click(); + $("a.ui-datepicker-next", dp).click(); + // contains non-breaking space + equal($("div.ui-datepicker-title").text(), + $( "<span>March 2010</span>" ).text(), "After next clicks"); + inp.datepicker("hide").datepicker("show"); + $("a.ui-datepicker-prev", dp).click(); + $("a.ui-datepicker-prev", dp).click(); + // contains non-breaking space + equal($("div.ui-datepicker-title").text(), + $( "<span>November 2009</span>" ).text(), "After prev clicks"); + inp.datepicker("hide"); +}); + +})(jQuery); diff --git a/apps/it/static/js/ui/tests/unit/datepicker/datepicker_methods.js b/apps/it/static/js/ui/tests/unit/datepicker/datepicker_methods.js new file mode 100644 index 0000000..e52e126 --- /dev/null +++ b/apps/it/static/js/ui/tests/unit/datepicker/datepicker_methods.js @@ -0,0 +1,125 @@ +/* + * datepicker_methods.js + */ +(function($) { + +module("datepicker: methods"); + +test("destroy", function() { + expect( 33 ); + var inl, + inp = TestHelpers.datepicker.init("#inp"); + ok(inp.is(".hasDatepicker"), "Default - marker class set"); + ok($.data(inp[0], TestHelpers.datepicker.PROP_NAME), "Default - instance present"); + ok(inp.next().is("#alt"), "Default - button absent"); + inp.datepicker("destroy"); + inp = $("#inp"); + ok(!inp.is(".hasDatepicker"), "Default - marker class cleared"); + ok(!$.data(inp[0], TestHelpers.datepicker.PROP_NAME), "Default - instance absent"); + ok(inp.next().is("#alt"), "Default - button absent"); + // With button + inp= TestHelpers.datepicker.init("#inp", {showOn: "both"}); + ok(inp.is(".hasDatepicker"), "Button - marker class set"); + ok($.data(inp[0], TestHelpers.datepicker.PROP_NAME), "Button - instance present"); + ok(inp.next().text() === "...", "Button - button added"); + inp.datepicker("destroy"); + inp = $("#inp"); + ok(!inp.is(".hasDatepicker"), "Button - marker class cleared"); + ok(!$.data(inp[0], TestHelpers.datepicker.PROP_NAME), "Button - instance absent"); + ok(inp.next().is("#alt"), "Button - button removed"); + // With append text + inp = TestHelpers.datepicker.init("#inp", {appendText: "Testing"}); + ok(inp.is(".hasDatepicker"), "Append - marker class set"); + ok($.data(inp[0], TestHelpers.datepicker.PROP_NAME), "Append - instance present"); + ok(inp.next().text() === "Testing", "Append - append text added"); + inp.datepicker("destroy"); + inp = $("#inp"); + ok(!inp.is(".hasDatepicker"), "Append - marker class cleared"); + ok(!$.data(inp[0], TestHelpers.datepicker.PROP_NAME), "Append - instance absent"); + ok(inp.next().is("#alt"), "Append - append text removed"); + // With both + inp= TestHelpers.datepicker.init("#inp", {showOn: "both", buttonImageOnly: true, + buttonImage: "images/calendar.gif", appendText: "Testing"}); + ok(inp.is(".hasDatepicker"), "Both - marker class set"); + ok($.data(inp[0], TestHelpers.datepicker.PROP_NAME), "Both - instance present"); + ok(inp.next()[0].nodeName.toLowerCase() === "img", "Both - button added"); + ok(inp.next().next().text() === "Testing", "Both - append text added"); + inp.datepicker("destroy"); + inp = $("#inp"); + ok(!inp.is(".hasDatepicker"), "Both - marker class cleared"); + ok(!$.data(inp[0], TestHelpers.datepicker.PROP_NAME), "Both - instance absent"); + ok(inp.next().is("#alt"), "Both - button and append text absent"); + // Inline + inl = TestHelpers.datepicker.init("#inl"); + ok(inl.is(".hasDatepicker"), "Inline - marker class set"); + ok(inl.html() !== "", "Inline - datepicker present"); + ok($.data(inl[0], TestHelpers.datepicker.PROP_NAME), "Inline - instance present"); + ok(inl.next().length === 0 || inl.next().is("p"), "Inline - button absent"); + inl.datepicker("destroy"); + inl = $("#inl"); + ok(!inl.is(".hasDatepicker"), "Inline - marker class cleared"); + ok(inl.html() === "", "Inline - datepicker absent"); + ok(!$.data(inl[0], TestHelpers.datepicker.PROP_NAME), "Inline - instance absent"); + ok(inl.next().length === 0 || inl.next().is("p"), "Inline - button absent"); +}); + +test("enableDisable", function() { + expect( 33 ); + var inl, dp, + inp = TestHelpers.datepicker.init("#inp"); + ok(!inp.datepicker("isDisabled"), "Enable/disable - initially marked as enabled"); + ok(!inp[0].disabled, "Enable/disable - field initially enabled"); + inp.datepicker("disable"); + ok(inp.datepicker("isDisabled"), "Enable/disable - now marked as disabled"); + ok(inp[0].disabled, "Enable/disable - field now disabled"); + inp.datepicker("enable"); + ok(!inp.datepicker("isDisabled"), "Enable/disable - now marked as enabled"); + ok(!inp[0].disabled, "Enable/disable - field now enabled"); + inp.datepicker("destroy"); + // With a button + inp = TestHelpers.datepicker.init("#inp", {showOn: "button"}); + ok(!inp.datepicker("isDisabled"), "Enable/disable button - initially marked as enabled"); + ok(!inp[0].disabled, "Enable/disable button - field initially enabled"); + ok(!inp.next("button")[0].disabled, "Enable/disable button - button initially enabled"); + inp.datepicker("disable"); + ok(inp.datepicker("isDisabled"), "Enable/disable button - now marked as disabled"); + ok(inp[0].disabled, "Enable/disable button - field now disabled"); + ok(inp.next("button")[0].disabled, "Enable/disable button - button now disabled"); + inp.datepicker("enable"); + ok(!inp.datepicker("isDisabled"), "Enable/disable button - now marked as enabled"); + ok(!inp[0].disabled, "Enable/disable button - field now enabled"); + ok(!inp.next("button")[0].disabled, "Enable/disable button - button now enabled"); + inp.datepicker("destroy"); + // With an image button + inp = TestHelpers.datepicker.init("#inp", {showOn: "button", buttonImageOnly: true, + buttonImage: "images/calendar.gif"}); + ok(!inp.datepicker("isDisabled"), "Enable/disable image - initially marked as enabled"); + ok(!inp[0].disabled, "Enable/disable image - field initially enabled"); + ok(parseFloat(inp.next("img").css("opacity")) === 1, "Enable/disable image - image initially enabled"); + inp.datepicker("disable"); + ok(inp.datepicker("isDisabled"), "Enable/disable image - now marked as disabled"); + ok(inp[0].disabled, "Enable/disable image - field now disabled"); + ok(parseFloat(inp.next("img").css("opacity")) !== 1, "Enable/disable image - image now disabled"); + inp.datepicker("enable"); + ok(!inp.datepicker("isDisabled"), "Enable/disable image - now marked as enabled"); + ok(!inp[0].disabled, "Enable/disable image - field now enabled"); + ok(parseFloat(inp.next("img").css("opacity")) === 1, "Enable/disable image - image now enabled"); + inp.datepicker("destroy"); + // Inline + inl = TestHelpers.datepicker.init("#inl", {changeYear: true}); + dp = $(".ui-datepicker-inline", inl); + ok(!inl.datepicker("isDisabled"), "Enable/disable inline - initially marked as enabled"); + ok(!dp.children().is(".ui-state-disabled"), "Enable/disable inline - not visually disabled initially"); + ok(!dp.find("select").prop("disabled"), "Enable/disable inline - form element enabled initially"); + inl.datepicker("disable"); + ok(inl.datepicker("isDisabled"), "Enable/disable inline - now marked as disabled"); + ok(dp.children().is(".ui-state-disabled"), "Enable/disable inline - visually disabled"); + ok(dp.find("select").prop("disabled"), "Enable/disable inline - form element disabled"); + inl.datepicker("enable"); + ok(!inl.datepicker("isDisabled"), "Enable/disable inline - now marked as enabled"); + ok(!dp.children().is(".ui-state-disabled"), "Enable/disable inline - not visiually disabled"); + ok(!dp.find("select").prop("disabled"), "Enable/disable inline - form element enabled"); + inl.datepicker("destroy"); +}); + +})(jQuery); diff --git a/apps/it/static/js/ui/tests/unit/datepicker/datepicker_options.js b/apps/it/static/js/ui/tests/unit/datepicker/datepicker_options.js new file mode 100644 index 0000000..b5dd4ca --- /dev/null +++ b/apps/it/static/js/ui/tests/unit/datepicker/datepicker_options.js @@ -0,0 +1,1113 @@ +/* + * datepicker_options.js + */ + +(function($) { + +module("datepicker: options"); + +test("setDefaults", function() { + expect( 3 ); + TestHelpers.datepicker.init("#inp"); + equal($.datepicker._defaults.showOn, "focus", "Initial showOn"); + $.datepicker.setDefaults({showOn: "button"}); + equal($.datepicker._defaults.showOn, "button", "Change default showOn"); + $.datepicker.setDefaults({showOn: "focus"}); + equal($.datepicker._defaults.showOn, "focus", "Restore showOn"); +}); + +test("option", function() { + expect( 17 ); + var inp = TestHelpers.datepicker.init("#inp"), + inst = $.data(inp[0], TestHelpers.datepicker.PROP_NAME); + // Set option + equal(inst.settings.showOn, null, "Initial setting showOn"); + equal($.datepicker._get(inst, "showOn"), "focus", "Initial instance showOn"); + equal($.datepicker._defaults.showOn, "focus", "Initial default showOn"); + inp.datepicker("option", "showOn", "button"); + equal(inst.settings.showOn, "button", "Change setting showOn"); + equal($.datepicker._get(inst, "showOn"), "button", "Change instance showOn"); + equal($.datepicker._defaults.showOn, "focus", "Retain default showOn"); + inp.datepicker("option", {showOn: "both"}); + equal(inst.settings.showOn, "both", "Change setting showOn"); + equal($.datepicker._get(inst, "showOn"), "both", "Change instance showOn"); + equal($.datepicker._defaults.showOn, "focus", "Retain default showOn"); + inp.datepicker("option", "showOn", undefined); + equal(inst.settings.showOn, null, "Clear setting showOn"); + equal($.datepicker._get(inst, "showOn"), "focus", "Restore instance showOn"); + equal($.datepicker._defaults.showOn, "focus", "Retain default showOn"); + // Get option + inp = TestHelpers.datepicker.init("#inp"); + equal(inp.datepicker("option", "showOn"), "focus", "Initial setting showOn"); + inp.datepicker("option", "showOn", "button"); + equal(inp.datepicker("option", "showOn"), "button", "Change instance showOn"); + inp.datepicker("option", "showOn", undefined); + equal(inp.datepicker("option", "showOn"), "focus", "Reset instance showOn"); + deepEqual(inp.datepicker("option", "all"), {showAnim: ""}, "Get instance settings"); + deepEqual(inp.datepicker("option", "defaults"), $.datepicker._defaults, + "Get default settings"); +}); + +test( "disabled", function() { + expect(8); + var inp = TestHelpers.datepicker.init("#inp"); + ok(!inp.datepicker("isDisabled"), "Initially marked as enabled"); + ok(!inp[0].disabled, "Field initially enabled"); + inp.datepicker("option", "disabled", true); + ok(inp.datepicker("isDisabled"), "Marked as disabled"); + ok(inp[0].disabled, "Field now disabled"); + inp.datepicker("option", "disabled", false); + ok(!inp.datepicker("isDisabled"), "Marked as enabled"); + ok(!inp[0].disabled, "Field now enabled"); + inp.datepicker("destroy"); + + inp = TestHelpers.datepicker.init("#inp", { disabled: true }); + ok(inp.datepicker("isDisabled"), "Initially marked as disabled"); + ok(inp[0].disabled, "Field initially disabled"); +}); + +test("change", function() { + expect( 12 ); + var inp = TestHelpers.datepicker.init("#inp"), + inst = $.data(inp[0], TestHelpers.datepicker.PROP_NAME); + equal(inst.settings.showOn, null, "Initial setting showOn"); + equal($.datepicker._get(inst, "showOn"), "focus", "Initial instance showOn"); + equal($.datepicker._defaults.showOn, "focus", "Initial default showOn"); + inp.datepicker("change", "showOn", "button"); + equal(inst.settings.showOn, "button", "Change setting showOn"); + equal($.datepicker._get(inst, "showOn"), "button", "Change instance showOn"); + equal($.datepicker._defaults.showOn, "focus", "Retain default showOn"); + inp.datepicker("change", {showOn: "both"}); + equal(inst.settings.showOn, "both", "Change setting showOn"); + equal($.datepicker._get(inst, "showOn"), "both", "Change instance showOn"); + equal($.datepicker._defaults.showOn, "focus", "Retain default showOn"); + inp.datepicker("change", "showOn", undefined); + equal(inst.settings.showOn, null, "Clear setting showOn"); + equal($.datepicker._get(inst, "showOn"), "focus", "Restore instance showOn"); + equal($.datepicker._defaults.showOn, "focus", "Retain default showOn"); +}); + +asyncTest( "invocation", function() { + var button, image, + isOldIE = $.ui.ie && ( !document.documentMode || document.documentMode < 9 ), + body = $( "body" ); + + expect( isOldIE ? 25 : 29 ); + + function step0() { + var inp = TestHelpers.datepicker.initNewInput(), + dp = $( "#ui-datepicker-div" ); + + button = inp.siblings( "button" ); + ok( button.length === 0, "Focus - button absent" ); + image = inp.siblings( "img" ); + ok( image.length === 0, "Focus - image absent" ); + + TestHelpers.datepicker.onFocus( inp, function() { + ok( dp.is( ":visible" ), "Focus - rendered on focus" ); + inp.simulate( "keydown", { keyCode: $.ui.keyCode.ESCAPE } ); + ok( !dp.is( ":visible" ), "Focus - hidden on exit" ); + step1(); + }); + } + + function step1() { + + var inp = TestHelpers.datepicker.initNewInput(), + dp = $( "#ui-datepicker-div" ); + + TestHelpers.datepicker.onFocus( inp, function() { + ok( dp.is( ":visible" ), "Focus - rendered on focus" ); + body.simulate( "mousedown", {} ); + ok( !dp.is( ":visible" ), "Focus - hidden on external click" ); + inp.datepicker( "hide" ).datepicker( "destroy" ); + + step2(); + }); + } + + function step2() { + var inp = TestHelpers.datepicker.initNewInput({ + showOn: "button", + buttonText: "Popup" + }), + dp = $( "#ui-datepicker-div" ); + + ok( !dp.is( ":visible" ), "Button - initially hidden" ); + button = inp.siblings( "button" ); + image = inp.siblings( "img" ); + ok( button.length === 1, "Button - button present" ); + ok( image.length === 0, "Button - image absent" ); + equal( button.text(), "Popup", "Button - button text" ); + + TestHelpers.datepicker.onFocus( inp, function() { + ok( !dp.is( ":visible" ), "Button - not rendered on focus" ); + button.click(); + ok( dp.is( ":visible" ), "Button - rendered on button click" ); + button.click(); + ok( !dp.is( ":visible" ), "Button - hidden on second button click" ); + inp.datepicker( "hide" ).datepicker( "destroy" ); + + step3(); + }); + } + + function step3() { + var inp = TestHelpers.datepicker.initNewInput({ + showOn: "button", + buttonImageOnly: true, + buttonImage: "images/calendar.gif", + buttonText: "Cal" + }), + dp = $( "#ui-datepicker-div" ); + + ok( !dp.is( ":visible" ), "Image button - initially hidden" ); + button = inp.siblings( "button" ); + ok( button.length === 0, "Image button - button absent" ); + image = inp.siblings( "img" ); + ok( image.length === 1, "Image button - image present" ); + ok( /images\/calendar\.gif$/.test( image.attr( "src" ) ), "Image button - image source" ); + equal( image.attr( "title" ), "Cal", "Image button - image text" ); + + TestHelpers.datepicker.onFocus( inp, function() { + ok( !dp.is( ":visible" ), "Image button - not rendered on focus" ); + image.click(); + ok( dp.is( ":visible" ), "Image button - rendered on image click" ); + image.click(); + ok( !dp.is( ":visible" ), "Image button - hidden on second image click" ); + inp.datepicker( "hide" ).datepicker( "destroy" ); + + step4(); + }); + } + + function step4() { + var inp = TestHelpers.datepicker.initNewInput({ + showOn: "both", + buttonImage: "images/calendar.gif" + }), + dp = $( "#ui-datepicker-div" ); + + ok( !dp.is( ":visible" ), "Both - initially hidden" ); + button = inp.siblings( "button" ); + ok( button.length === 1, "Both - button present" ); + image = inp.siblings( "img" ); + ok( image.length === 0, "Both - image absent" ); + image = button.children( "img" ); + ok( image.length === 1, "Both - button image present" ); + + // TODO: occasionally this test flakily fails to focus in IE8 in browserstack + if ( !isOldIE ) { + TestHelpers.datepicker.onFocus( inp, function() { + ok( dp.is( ":visible" ), "Both - rendered on focus" ); + body.simulate( "mousedown", {} ); + ok( !dp.is( ":visible" ), "Both - hidden on external click" ); + button.click(); + ok( dp.is( ":visible" ), "Both - rendered on button click" ); + button.click(); + ok( !dp.is( ":visible" ), "Both - hidden on second button click" ); + inp.datepicker( "hide" ).datepicker( "destroy" ); + + start(); + }); + } else { + start(); + } + } + + step0(); +}); + +test("otherMonths", function() { + expect( 8 ); + var inp = TestHelpers.datepicker.init("#inp"), + pop = $("#ui-datepicker-div"); + inp.val("06/01/2009").datepicker("show"); + equal(pop.find("tbody").text(), + // support: IE <9, jQuery <1.8 + // In IE7/8 with jQuery <1.8, encoded spaces behave in strange ways + $( "<span>\u00a0123456789101112131415161718192021222324252627282930\u00a0\u00a0\u00a0\u00a0</span>" ).text(), + "Other months - none"); + ok(pop.find("td:last *").length === 0, "Other months - no content"); + inp.datepicker("hide").datepicker("option", "showOtherMonths", true).datepicker("show"); + equal(pop.find("tbody").text(), "311234567891011121314151617181920212223242526272829301234", + "Other months - show"); + ok(pop.find("td:last span").length === 1, "Other months - span content"); + inp.datepicker("hide").datepicker("option", "selectOtherMonths", true).datepicker("show"); + equal(pop.find("tbody").text(), "311234567891011121314151617181920212223242526272829301234", + "Other months - select"); + ok(pop.find("td:last a").length === 1, "Other months - link content"); + inp.datepicker("hide").datepicker("option", "showOtherMonths", false).datepicker("show"); + equal(pop.find("tbody").text(), + // support: IE <9, jQuery <1.8 + // In IE7/8 with jQuery <1.8, encoded spaces behave in strange ways + $( "<span>\u00a0123456789101112131415161718192021222324252627282930\u00a0\u00a0\u00a0\u00a0</span>" ).text(), + "Other months - none"); + ok(pop.find("td:last *").length === 0, "Other months - no content"); +}); + +test("defaultDate", function() { + expect( 16 ); + var inp = TestHelpers.datepicker.init("#inp"), + date = new Date(); + inp.val("").datepicker("show"). + simulate("keydown", {keyCode: $.ui.keyCode.ENTER}); + TestHelpers.datepicker.equalsDate(inp.datepicker("getDate"), date, "Default date null"); + + // Numeric values + inp.datepicker("option", {defaultDate: -2}). + datepicker("hide").val("").datepicker("show"). + simulate("keydown", {keyCode: $.ui.keyCode.ENTER}); + date.setDate(date.getDate() - 2); + TestHelpers.datepicker.equalsDate(inp.datepicker("getDate"), date, "Default date -2"); + + date = new Date(); + inp.datepicker("option", {defaultDate: 3}). + datepicker("hide").val("").datepicker("show"). + simulate("keydown", {keyCode: $.ui.keyCode.ENTER}); + date.setDate(date.getDate() + 3); + TestHelpers.datepicker.equalsDate(inp.datepicker("getDate"), date, "Default date 3"); + + date = new Date(); + inp.datepicker("option", {defaultDate: 1 / "a"}). + datepicker("hide").val("").datepicker("show"). + simulate("keydown", {keyCode: $.ui.keyCode.ENTER}); + TestHelpers.datepicker.equalsDate(inp.datepicker("getDate"), date, "Default date NaN"); + + // String offset values + inp.datepicker("option", {defaultDate: "-1d"}). + datepicker("hide").val("").datepicker("show"). + simulate("keydown", {keyCode: $.ui.keyCode.ENTER}); + date.setDate(date.getDate() - 1); + TestHelpers.datepicker.equalsDate(inp.datepicker("getDate"), date, "Default date -1d"); + inp.datepicker("option", {defaultDate: "+3D"}). + datepicker("hide").val("").datepicker("show"). + simulate("keydown", {keyCode: $.ui.keyCode.ENTER}); + date.setDate(date.getDate() + 4); + TestHelpers.datepicker.equalsDate(inp.datepicker("getDate"), date, "Default date +3D"); + inp.datepicker("option", {defaultDate: " -2 w "}). + datepicker("hide").val("").datepicker("show"). + simulate("keydown", {keyCode: $.ui.keyCode.ENTER}); + date = new Date(); + date.setDate(date.getDate() - 14); + TestHelpers.datepicker.equalsDate(inp.datepicker("getDate"), date, "Default date -2 w"); + inp.datepicker("option", {defaultDate: "+1 W"}). + datepicker("hide").val("").datepicker("show"). + simulate("keydown", {keyCode: $.ui.keyCode.ENTER}); + date.setDate(date.getDate() + 21); + TestHelpers.datepicker.equalsDate(inp.datepicker("getDate"), date, "Default date +1 W"); + inp.datepicker("option", {defaultDate: " -1 m "}). + datepicker("hide").val("").datepicker("show"). + simulate("keydown", {keyCode: $.ui.keyCode.ENTER}); + date = TestHelpers.datepicker.addMonths(new Date(), -1); + TestHelpers.datepicker.equalsDate(inp.datepicker("getDate"), date, "Default date -1 m"); + inp.datepicker("option", {defaultDate: "+2M"}). + datepicker("hide").val("").datepicker("show"). + simulate("keydown", {keyCode: $.ui.keyCode.ENTER}); + date = TestHelpers.datepicker.addMonths(new Date(), 2); + TestHelpers.datepicker.equalsDate(inp.datepicker("getDate"), date, "Default date +2M"); + inp.datepicker("option", {defaultDate: "-2y"}). + datepicker("hide").val("").datepicker("show"). + simulate("keydown", {keyCode: $.ui.keyCode.ENTER}); + date = new Date(); + date.setFullYear(date.getFullYear() - 2); + TestHelpers.datepicker.equalsDate(inp.datepicker("getDate"), date, "Default date -2y"); + inp.datepicker("option", {defaultDate: "+1 Y "}). + datepicker("hide").val("").datepicker("show"). + simulate("keydown", {keyCode: $.ui.keyCode.ENTER}); + date.setFullYear(date.getFullYear() + 3); + TestHelpers.datepicker.equalsDate(inp.datepicker("getDate"), date, "Default date +1 Y"); + inp.datepicker("option", {defaultDate: "+1M +10d"}). + datepicker("hide").val("").datepicker("show"). + simulate("keydown", {keyCode: $.ui.keyCode.ENTER}); + date = TestHelpers.datepicker.addMonths(new Date(), 1); + date.setDate(date.getDate() + 10); + TestHelpers.datepicker.equalsDate(inp.datepicker("getDate"), date, "Default date +1M +10d"); + // String date values + inp.datepicker("option", {defaultDate: "07/04/2007"}). + datepicker("hide").val("").datepicker("show"). + simulate("keydown", {keyCode: $.ui.keyCode.ENTER}); + date = new Date(2007, 7 - 1, 4); + TestHelpers.datepicker.equalsDate(inp.datepicker("getDate"), date, "Default date 07/04/2007"); + inp.datepicker("option", {dateFormat: "yy-mm-dd", defaultDate: "2007-04-02"}). + datepicker("hide").val("").datepicker("show"). + simulate("keydown", {keyCode: $.ui.keyCode.ENTER}); + date = new Date(2007, 4 - 1, 2); + TestHelpers.datepicker.equalsDate(inp.datepicker("getDate"), date, "Default date 2007-04-02"); + // Date value + date = new Date(2007, 1 - 1, 26); + inp.datepicker("option", {dateFormat: "mm/dd/yy", defaultDate: date}). + datepicker("hide").val("").datepicker("show"). + simulate("keydown", {keyCode: $.ui.keyCode.ENTER}); + TestHelpers.datepicker.equalsDate(inp.datepicker("getDate"), date, "Default date 01/26/2007"); +}); + +test("miscellaneous", function() { + expect( 19 ); + var curYear, longNames, shortNames, date, + dp = $("#ui-datepicker-div"), + inp = TestHelpers.datepicker.init("#inp"); + // Year range + function genRange(start, offset) { + var i = start, + range = ""; + for (; i < start + offset; i++) { + range += i; + } + return range; + } + curYear = new Date().getFullYear(); + inp.val("02/04/2008").datepicker("show"); + equal(dp.find(".ui-datepicker-year").text(), "2008", "Year range - read-only default"); + inp.datepicker("hide").datepicker("option", {changeYear: true}).datepicker("show"); + equal(dp.find(".ui-datepicker-year").text(), genRange(2008 - 10, 21), "Year range - changeable default"); + inp.datepicker("hide").datepicker("option", {yearRange: "c-6:c+2", changeYear: true}).datepicker("show"); + equal(dp.find(".ui-datepicker-year").text(), genRange(2008 - 6, 9), "Year range - c-6:c+2"); + inp.datepicker("hide").datepicker("option", {yearRange: "2000:2010", changeYear: true}).datepicker("show"); + equal(dp.find(".ui-datepicker-year").text(), genRange(2000, 11), "Year range - 2000:2010"); + inp.datepicker("hide").datepicker("option", {yearRange: "-5:+3", changeYear: true}).datepicker("show"); + equal(dp.find(".ui-datepicker-year").text(), genRange(curYear - 5, 9), "Year range - -5:+3"); + inp.datepicker("hide").datepicker("option", {yearRange: "2000:-5", changeYear: true}).datepicker("show"); + equal(dp.find(".ui-datepicker-year").text(), genRange(2000, curYear - 2004), "Year range - 2000:-5"); + inp.datepicker("hide").datepicker("option", {yearRange: "", changeYear: true}).datepicker("show"); + equal(dp.find(".ui-datepicker-year").text(), genRange(curYear, 1), "Year range - -6:+2"); + + // Navigation as date format + inp.datepicker("option", {showButtonPanel: true}); + equal(dp.find(".ui-datepicker-prev").text(), "Prev", "Navigation prev - default"); + equal(dp.find(".ui-datepicker-current").text(), "Today", "Navigation current - default"); + equal(dp.find(".ui-datepicker-next").text(), "Next", "Navigation next - default"); + inp.datepicker("hide").datepicker("option", {navigationAsDateFormat: true, prevText: "< M", currentText: "MM", nextText: "M >"}). + val("02/04/2008").datepicker("show"); + longNames = $.datepicker.regional[""].monthNames; + shortNames = $.datepicker.regional[""].monthNamesShort; + date = new Date(); + equal(dp.find(".ui-datepicker-prev").text(), "< " + shortNames[0], "Navigation prev - as date format"); + equal(dp.find(".ui-datepicker-current").text(), + longNames[date.getMonth()], "Navigation current - as date format"); + equal(dp.find(".ui-datepicker-next").text(), + shortNames[2] + " >", "Navigation next - as date format"); + inp.simulate("keydown", {keyCode: $.ui.keyCode.PAGE_DOWN}); + equal(dp.find(".ui-datepicker-prev").text(), + "< " + shortNames[1], "Navigation prev - as date format + pgdn"); + equal(dp.find(".ui-datepicker-current").text(), + longNames[date.getMonth()], "Navigation current - as date format + pgdn"); + equal(dp.find(".ui-datepicker-next").text(), + shortNames[3] + " >", "Navigation next - as date format + pgdn"); + inp.datepicker("hide").datepicker("option", {gotoCurrent: true}). + val("02/04/2008").datepicker("show"); + equal(dp.find(".ui-datepicker-prev").text(), + "< " + shortNames[0], "Navigation prev - as date format + goto current"); + equal(dp.find(".ui-datepicker-current").text(), + longNames[1], "Navigation current - as date format + goto current"); + equal(dp.find(".ui-datepicker-next").text(), + shortNames[2] + " >", "Navigation next - as date format + goto current"); +}); + +test("minMax", function() { + expect( 23 ); + var date, + inp = TestHelpers.datepicker.init("#inp"), + dp = $("#ui-datepicker-div"), + lastYear = new Date(2007, 6 - 1, 4), + nextYear = new Date(2009, 6 - 1, 4), + minDate = new Date(2008, 2 - 1, 29), + maxDate = new Date(2008, 12 - 1, 7); + inp.val("06/04/2008").datepicker("show"); + inp.simulate("keydown", {ctrlKey: true, keyCode: $.ui.keyCode.PAGE_UP}). + simulate("keydown", {keyCode: $.ui.keyCode.ENTER}); + TestHelpers.datepicker.equalsDate(inp.datepicker("getDate"), lastYear, + "Min/max - null, null - ctrl+pgup"); + inp.val("06/04/2008").datepicker("show"); + inp.simulate("keydown", {ctrlKey: true, keyCode: $.ui.keyCode.PAGE_DOWN}). + simulate("keydown", {keyCode: $.ui.keyCode.ENTER}); + TestHelpers.datepicker.equalsDate(inp.datepicker("getDate"), nextYear, + "Min/max - null, null - ctrl+pgdn"); + inp.datepicker("option", {minDate: minDate}). + datepicker("hide").val("06/04/2008").datepicker("show"); + inp.simulate("keydown", {ctrlKey: true, keyCode: $.ui.keyCode.PAGE_UP}). + simulate("keydown", {keyCode: $.ui.keyCode.ENTER}); + TestHelpers.datepicker.equalsDate(inp.datepicker("getDate"), minDate, + "Min/max - 02/29/2008, null - ctrl+pgup"); + inp.val("06/04/2008").datepicker("show"); + inp.simulate("keydown", {ctrlKey: true, keyCode: $.ui.keyCode.PAGE_DOWN}). + simulate("keydown", {keyCode: $.ui.keyCode.ENTER}); + TestHelpers.datepicker.equalsDate(inp.datepicker("getDate"), nextYear, + "Min/max - 02/29/2008, null - ctrl+pgdn"); + inp.datepicker("option", {maxDate: maxDate}). + datepicker("hide").val("06/04/2008").datepicker("show"); + inp.simulate("keydown", {ctrlKey: true, keyCode: $.ui.keyCode.PAGE_UP}). + simulate("keydown", {keyCode: $.ui.keyCode.ENTER}); + TestHelpers.datepicker.equalsDate(inp.datepicker("getDate"), minDate, + "Min/max - 02/29/2008, 12/07/2008 - ctrl+pgup"); + inp.val("06/04/2008").datepicker("show"); + inp.simulate("keydown", {ctrlKey: true, keyCode: $.ui.keyCode.PAGE_DOWN}). + simulate("keydown", {keyCode: $.ui.keyCode.ENTER}); + TestHelpers.datepicker.equalsDate(inp.datepicker("getDate"), maxDate, + "Min/max - 02/29/2008, 12/07/2008 - ctrl+pgdn"); + inp.datepicker("option", {minDate: null}). + datepicker("hide").val("06/04/2008").datepicker("show"); + inp.simulate("keydown", {ctrlKey: true, keyCode: $.ui.keyCode.PAGE_UP}). + simulate("keydown", {keyCode: $.ui.keyCode.ENTER}); + TestHelpers.datepicker.equalsDate(inp.datepicker("getDate"), lastYear, + "Min/max - null, 12/07/2008 - ctrl+pgup"); + inp.val("06/04/2008").datepicker("show"); + inp.simulate("keydown", {ctrlKey: true, keyCode: $.ui.keyCode.PAGE_DOWN}). + simulate("keydown", {keyCode: $.ui.keyCode.ENTER}); + TestHelpers.datepicker.equalsDate(inp.datepicker("getDate"), maxDate, + "Min/max - null, 12/07/2008 - ctrl+pgdn"); + // Relative dates + date = new Date(); + date.setDate(date.getDate() - 7); + inp.datepicker("option", {minDate: "-1w", maxDate: "+1 M +10 D "}). + datepicker("hide").val("").datepicker("show"); + inp.simulate("keydown", {ctrlKey: true, keyCode: $.ui.keyCode.PAGE_UP}). + simulate("keydown", {keyCode: $.ui.keyCode.ENTER}); + TestHelpers.datepicker.equalsDate(inp.datepicker("getDate"), date, + "Min/max - -1w, +1 M +10 D - ctrl+pgup"); + date = TestHelpers.datepicker.addMonths(new Date(), 1); + date.setDate(date.getDate() + 10); + inp.val("").datepicker("show"); + inp.simulate("keydown", {ctrlKey: true, keyCode: $.ui.keyCode.PAGE_DOWN}). + simulate("keydown", {keyCode: $.ui.keyCode.ENTER}); + TestHelpers.datepicker.equalsDate(inp.datepicker("getDate"), date, + "Min/max - -1w, +1 M +10 D - ctrl+pgdn"); + // With existing date + inp = TestHelpers.datepicker.init("#inp"); + inp.val("06/04/2008").datepicker("option", {minDate: minDate}); + TestHelpers.datepicker.equalsDate(inp.datepicker("getDate"), new Date(2008, 6 - 1, 4), "Min/max - setDate > min"); + inp.datepicker("option", {minDate: null}).val("01/04/2008").datepicker("option", {minDate: minDate}); + TestHelpers.datepicker.equalsDate(inp.datepicker("getDate"), minDate, "Min/max - setDate < min"); + inp.datepicker("option", {minDate: null}).val("06/04/2008").datepicker("option", {maxDate: maxDate}); + TestHelpers.datepicker.equalsDate(inp.datepicker("getDate"), new Date(2008, 6 - 1, 4), "Min/max - setDate < max"); + inp.datepicker("option", {maxDate: null}).val("01/04/2009").datepicker("option", {maxDate: maxDate}); + TestHelpers.datepicker.equalsDate(inp.datepicker("getDate"), maxDate, "Min/max - setDate > max"); + inp.datepicker("option", {maxDate: null}).val("01/04/2008").datepicker("option", {minDate: minDate, maxDate: maxDate}); + TestHelpers.datepicker.equalsDate(inp.datepicker("getDate"), minDate, "Min/max - setDate < min"); + inp.datepicker("option", {maxDate: null}).val("06/04/2008").datepicker("option", {minDate: minDate, maxDate: maxDate}); + TestHelpers.datepicker.equalsDate(inp.datepicker("getDate"), new Date(2008, 6 - 1, 4), "Min/max - setDate > min, < max"); + inp.datepicker("option", {maxDate: null}).val("01/04/2009").datepicker("option", {minDate: minDate, maxDate: maxDate}); + TestHelpers.datepicker.equalsDate(inp.datepicker("getDate"), maxDate, "Min/max - setDate > max"); + + inp.datepicker("option", {yearRange: "-0:+1"}).val("01/01/" + new Date().getFullYear()); + ok(dp.find(".ui-datepicker-prev").hasClass("ui-state-disabled"), "Year Range Test - previous button disabled at 1/1/minYear"); + inp.datepicker("setDate", "12/30/" + new Date().getFullYear()); + ok(dp.find(".ui-datepicker-next").hasClass("ui-state-disabled"), "Year Range Test - next button disabled at 12/30/maxYear"); + + inp.datepicker("option", { + minDate: new Date(1900, 0, 1), + maxDate: "-6Y", + yearRange: "1900:-6" + }).val( "" ); + ok(dp.find(".ui-datepicker-next").hasClass("ui-state-disabled"), "Year Range Test - next button disabled"); + ok(!dp.find(".ui-datepicker-prev").hasClass("ui-state-disabled"), "Year Range Test - prev button enabled"); + + inp.datepicker("option", { + minDate: new Date(1900, 0, 1), + maxDate: "1/25/2007", + yearRange: "1900:2007" + }).val( "" ); + ok(dp.find(".ui-datepicker-next").hasClass("ui-state-disabled"), "Year Range Test - next button disabled"); + ok(!dp.find(".ui-datepicker-prev").hasClass("ui-state-disabled"), "Year Range Test - prev button enabled"); +}); + +test("setDate", function() { + expect( 24 ); + var inl, alt, minDate, maxDate, dateAndTimeToSet, dateAndTimeClone, + inp = TestHelpers.datepicker.init("#inp"), + date1 = new Date(2008, 6 - 1, 4), + date2 = new Date(); + ok(inp.datepicker("getDate") == null, "Set date - default"); + inp.datepicker("setDate", date1); + TestHelpers.datepicker.equalsDate(inp.datepicker("getDate"), date1, "Set date - 2008-06-04"); + date1 = new Date(); + date1.setDate(date1.getDate() + 7); + inp.datepicker("setDate", +7); + TestHelpers.datepicker.equalsDate(inp.datepicker("getDate"), date1, "Set date - +7"); + date2.setFullYear(date2.getFullYear() + 2); + inp.datepicker("setDate", "+2y"); + TestHelpers.datepicker.equalsDate(inp.datepicker("getDate"), date2, "Set date - +2y"); + inp.datepicker("setDate", date1, date2); + TestHelpers.datepicker.equalsDate(inp.datepicker("getDate"), date1, "Set date - two dates"); + inp.datepicker("setDate"); + ok(inp.datepicker("getDate") == null, "Set date - null"); + // Relative to current date + date1 = new Date(); + date1.setDate(date1.getDate() + 7); + inp.datepicker("setDate", "c +7"); + TestHelpers.datepicker.equalsDate(inp.datepicker("getDate"), date1, "Set date - c +7"); + date1.setDate(date1.getDate() + 7); + inp.datepicker("setDate", "c+7"); + TestHelpers.datepicker.equalsDate(inp.datepicker("getDate"), date1, "Set date - c+7"); + date1.setDate(date1.getDate() - 21); + inp.datepicker("setDate", "c -3 w"); + TestHelpers.datepicker.equalsDate(inp.datepicker("getDate"), date1, "Set date - c -3 w"); + // Inline + inl = TestHelpers.datepicker.init("#inl"); + date1 = new Date(2008, 6 - 1, 4); + date2 = new Date(); + TestHelpers.datepicker.equalsDate(inl.datepicker("getDate"), date2, "Set date inline - default"); + inl.datepicker("setDate", date1); + TestHelpers.datepicker.equalsDate(inl.datepicker("getDate"), date1, "Set date inline - 2008-06-04"); + date1 = new Date(); + date1.setDate(date1.getDate() + 7); + inl.datepicker("setDate", +7); + TestHelpers.datepicker.equalsDate(inl.datepicker("getDate"), date1, "Set date inline - +7"); + date2.setFullYear(date2.getFullYear() + 2); + inl.datepicker("setDate", "+2y"); + TestHelpers.datepicker.equalsDate(inl.datepicker("getDate"), date2, "Set date inline - +2y"); + inl.datepicker("setDate", date1, date2); + TestHelpers.datepicker.equalsDate(inl.datepicker("getDate"), date1, "Set date inline - two dates"); + inl.datepicker("setDate"); + ok(inl.datepicker("getDate") == null, "Set date inline - null"); + // Alternate field + alt = $("#alt"); + inp.datepicker("option", {altField: "#alt", altFormat: "yy-mm-dd"}); + date1 = new Date(2008, 6 - 1, 4); + inp.datepicker("setDate", date1); + equal(inp.val(), "06/04/2008", "Set date alternate - 06/04/2008"); + equal(alt.val(), "2008-06-04", "Set date alternate - 2008-06-04"); + // With minimum/maximum + inp = TestHelpers.datepicker.init("#inp"); + date1 = new Date(2008, 1 - 1, 4); + date2 = new Date(2008, 6 - 1, 4); + minDate = new Date(2008, 2 - 1, 29); + maxDate = new Date(2008, 3 - 1, 28); + inp.val("").datepicker("option", {minDate: minDate}).datepicker("setDate", date2); + TestHelpers.datepicker.equalsDate(inp.datepicker("getDate"), date2, "Set date min/max - setDate > min"); + inp.datepicker("setDate", date1); + TestHelpers.datepicker.equalsDate(inp.datepicker("getDate"), minDate, "Set date min/max - setDate < min"); + inp.val("").datepicker("option", {maxDate: maxDate, minDate: null}).datepicker("setDate", date1); + TestHelpers.datepicker.equalsDate(inp.datepicker("getDate"), date1, "Set date min/max - setDate < max"); + inp.datepicker("setDate", date2); + TestHelpers.datepicker.equalsDate(inp.datepicker("getDate"), maxDate, "Set date min/max - setDate > max"); + inp.val("").datepicker("option", {minDate: minDate}).datepicker("setDate", date1); + TestHelpers.datepicker.equalsDate(inp.datepicker("getDate"), minDate, "Set date min/max - setDate < min"); + inp.datepicker("setDate", date2); + TestHelpers.datepicker.equalsDate(inp.datepicker("getDate"), maxDate, "Set date min/max - setDate > max"); + dateAndTimeToSet = new Date(2008, 3 - 1, 28, 1, 11, 0); + dateAndTimeClone = new Date(2008, 3 - 1, 28, 1, 11, 0); + inp.datepicker("setDate", dateAndTimeToSet); + equal(dateAndTimeToSet.getTime(), dateAndTimeClone.getTime(), "Date object passed should not be changed by setDate"); +}); + +test("altField", function() { + expect( 10 ); + var inp = TestHelpers.datepicker.init("#inp"), + alt = $("#alt"); + // No alternate field set + alt.val(""); + inp.val("06/04/2008").datepicker("show"); + inp.simulate("keydown", {keyCode: $.ui.keyCode.ENTER}); + equal(inp.val(), "06/04/2008", "Alt field - dp - enter"); + equal(alt.val(), "", "Alt field - alt not set"); + // Alternate field set + alt.val(""); + inp.datepicker("option", {altField: "#alt", altFormat: "yy-mm-dd"}). + val("06/04/2008").datepicker("show"); + inp.simulate("keydown", {keyCode: $.ui.keyCode.ENTER}); + equal(inp.val(), "06/04/2008", "Alt field - dp - enter"); + equal(alt.val(), "2008-06-04", "Alt field - alt - enter"); + // Move from initial date + alt.val(""); + inp.val("06/04/2008").datepicker("show"); + inp.simulate("keydown", {keyCode: $.ui.keyCode.PAGE_DOWN}). + simulate("keydown", {keyCode: $.ui.keyCode.ENTER}); + equal(inp.val(), "07/04/2008", "Alt field - dp - pgdn"); + equal(alt.val(), "2008-07-04", "Alt field - alt - pgdn"); + // Alternate field set - closed + alt.val(""); + inp.val("06/04/2008").datepicker("show"); + inp.simulate("keydown", {keyCode: $.ui.keyCode.PAGE_DOWN}). + simulate("keydown", {keyCode: $.ui.keyCode.ESCAPE}); + equal(inp.val(), "06/04/2008", "Alt field - dp - pgdn/esc"); + equal(alt.val(), "", "Alt field - alt - pgdn/esc"); + // Clear date and alternate + alt.val(""); + inp.val("06/04/2008").datepicker("show"); + inp.simulate("keydown", {ctrlKey: true, keyCode: $.ui.keyCode.END}); + equal(inp.val(), "", "Alt field - dp - ctrl+end"); + equal(alt.val(), "", "Alt field - alt - ctrl+end"); +}); + +test("autoSize", function() { + expect( 15 ); + var inp = TestHelpers.datepicker.init("#inp"); + equal(inp.prop("size"), 20, "Auto size - default"); + inp.datepicker("option", "autoSize", true); + equal(inp.prop("size"), 10, "Auto size - mm/dd/yy"); + inp.datepicker("option", "dateFormat", "m/d/yy"); + equal(inp.prop("size"), 10, "Auto size - m/d/yy"); + inp.datepicker("option", "dateFormat", "D M d yy"); + equal(inp.prop("size"), 15, "Auto size - D M d yy"); + inp.datepicker("option", "dateFormat", "DD, MM dd, yy"); + equal(inp.prop("size"), 29, "Auto size - DD, MM dd, yy"); + + // French + inp.datepicker("option", $.extend({autoSize: false}, $.datepicker.regional.fr)); + equal(inp.prop("size"), 29, "Auto size - fr - default"); + inp.datepicker("option", "autoSize", true); + equal(inp.prop("size"), 10, "Auto size - fr - dd/mm/yy"); + inp.datepicker("option", "dateFormat", "m/d/yy"); + equal(inp.prop("size"), 10, "Auto size - fr - m/d/yy"); + inp.datepicker("option", "dateFormat", "D M d yy"); + equal(inp.prop("size"), 18, "Auto size - fr - D M d yy"); + inp.datepicker("option", "dateFormat", "DD, MM dd, yy"); + equal(inp.prop("size"), 28, "Auto size - fr - DD, MM dd, yy"); + + // Hebrew + inp.datepicker("option", $.extend({autoSize: false}, $.datepicker.regional.he)); + equal(inp.prop("size"), 28, "Auto size - he - default"); + inp.datepicker("option", "autoSize", true); + equal(inp.prop("size"), 10, "Auto size - he - dd/mm/yy"); + inp.datepicker("option", "dateFormat", "m/d/yy"); + equal(inp.prop("size"), 10, "Auto size - he - m/d/yy"); + inp.datepicker("option", "dateFormat", "D M d yy"); + equal(inp.prop("size"), 16, "Auto size - he - D M d yy"); + inp.datepicker("option", "dateFormat", "DD, MM dd, yy"); + equal(inp.prop("size"), 23, "Auto size - he - DD, MM dd, yy"); +}); + +test("daylightSaving", function() { + expect( 25 ); + var inp = TestHelpers.datepicker.init("#inp"), + dp = $("#ui-datepicker-div"); + ok(true, "Daylight saving - " + new Date()); + // Australia, Sydney - AM change, southern hemisphere + inp.val("04/01/2008").datepicker("show"); + $(".ui-datepicker-calendar td:eq(6) a", dp).simulate("click"); + equal(inp.val(), "04/05/2008", "Daylight saving - Australia 04/05/2008"); + inp.val("04/01/2008").datepicker("show"); + $(".ui-datepicker-calendar td:eq(7) a", dp).simulate("click"); + equal(inp.val(), "04/06/2008", "Daylight saving - Australia 04/06/2008"); + inp.val("04/01/2008").datepicker("show"); + $(".ui-datepicker-calendar td:eq(8) a", dp).simulate("click"); + equal(inp.val(), "04/07/2008", "Daylight saving - Australia 04/07/2008"); + inp.val("10/01/2008").datepicker("show"); + $(".ui-datepicker-calendar td:eq(6) a", dp).simulate("click"); + equal(inp.val(), "10/04/2008", "Daylight saving - Australia 10/04/2008"); + inp.val("10/01/2008").datepicker("show"); + $(".ui-datepicker-calendar td:eq(7) a", dp).simulate("click"); + equal(inp.val(), "10/05/2008", "Daylight saving - Australia 10/05/2008"); + inp.val("10/01/2008").datepicker("show"); + $(".ui-datepicker-calendar td:eq(8) a", dp).simulate("click"); + equal(inp.val(), "10/06/2008", "Daylight saving - Australia 10/06/2008"); + // Brasil, Brasilia - midnight change, southern hemisphere + inp.val("02/01/2008").datepicker("show"); + $(".ui-datepicker-calendar td:eq(20) a", dp).simulate("click"); + equal(inp.val(), "02/16/2008", "Daylight saving - Brasil 02/16/2008"); + inp.val("02/01/2008").datepicker("show"); + $(".ui-datepicker-calendar td:eq(21) a", dp).simulate("click"); + equal(inp.val(), "02/17/2008", "Daylight saving - Brasil 02/17/2008"); + inp.val("02/01/2008").datepicker("show"); + $(".ui-datepicker-calendar td:eq(22) a", dp).simulate("click"); + equal(inp.val(), "02/18/2008", "Daylight saving - Brasil 02/18/2008"); + inp.val("10/01/2008").datepicker("show"); + $(".ui-datepicker-calendar td:eq(13) a", dp).simulate("click"); + equal(inp.val(), "10/11/2008", "Daylight saving - Brasil 10/11/2008"); + inp.val("10/01/2008").datepicker("show"); + $(".ui-datepicker-calendar td:eq(14) a", dp).simulate("click"); + equal(inp.val(), "10/12/2008", "Daylight saving - Brasil 10/12/2008"); + inp.val("10/01/2008").datepicker("show"); + $(".ui-datepicker-calendar td:eq(15) a", dp).simulate("click"); + equal(inp.val(), "10/13/2008", "Daylight saving - Brasil 10/13/2008"); + // Lebanon, Beirut - midnight change, northern hemisphere + inp.val("03/01/2008").datepicker("show"); + $(".ui-datepicker-calendar td:eq(34) a", dp).simulate("click"); + equal(inp.val(), "03/29/2008", "Daylight saving - Lebanon 03/29/2008"); + inp.val("03/01/2008").datepicker("show"); + $(".ui-datepicker-calendar td:eq(35) a", dp).simulate("click"); + equal(inp.val(), "03/30/2008", "Daylight saving - Lebanon 03/30/2008"); + inp.val("03/01/2008").datepicker("show"); + $(".ui-datepicker-calendar td:eq(36) a", dp).simulate("click"); + equal(inp.val(), "03/31/2008", "Daylight saving - Lebanon 03/31/2008"); + inp.val("10/01/2008").datepicker("show"); + $(".ui-datepicker-calendar td:eq(27) a", dp).simulate("click"); + equal(inp.val(), "10/25/2008", "Daylight saving - Lebanon 10/25/2008"); + inp.val("10/01/2008").datepicker("show"); + $(".ui-datepicker-calendar td:eq(28) a", dp).simulate("click"); + equal(inp.val(), "10/26/2008", "Daylight saving - Lebanon 10/26/2008"); + inp.val("10/01/2008").datepicker("show"); + $(".ui-datepicker-calendar td:eq(29) a", dp).simulate("click"); + equal(inp.val(), "10/27/2008", "Daylight saving - Lebanon 10/27/2008"); + // US, Eastern - AM change, northern hemisphere + inp.val("03/01/2008").datepicker("show"); + $(".ui-datepicker-calendar td:eq(13) a", dp).simulate("click"); + equal(inp.val(), "03/08/2008", "Daylight saving - US 03/08/2008"); + inp.val("03/01/2008").datepicker("show"); + $(".ui-datepicker-calendar td:eq(14) a", dp).simulate("click"); + equal(inp.val(), "03/09/2008", "Daylight saving - US 03/09/2008"); + inp.val("03/01/2008").datepicker("show"); + $(".ui-datepicker-calendar td:eq(15) a", dp).simulate("click"); + equal(inp.val(), "03/10/2008", "Daylight saving - US 03/10/2008"); + inp.val("11/01/2008").datepicker("show"); + $(".ui-datepicker-calendar td:eq(6) a", dp).simulate("click"); + equal(inp.val(), "11/01/2008", "Daylight saving - US 11/01/2008"); + inp.val("11/01/2008").datepicker("show"); + $(".ui-datepicker-calendar td:eq(7) a", dp).simulate("click"); + equal(inp.val(), "11/02/2008", "Daylight saving - US 11/02/2008"); + inp.val("11/01/2008").datepicker("show"); + $(".ui-datepicker-calendar td:eq(8) a", dp).simulate("click"); + equal(inp.val(), "11/03/2008", "Daylight saving - US 11/03/2008"); +}); + +var beforeShowThis = null, + beforeShowInput = null, + beforeShowInst = null, + beforeShowDayThis = null, + beforeShowDayOK = true; + + +function beforeAll(input, inst) { + beforeShowThis = this; + beforeShowInput = input; + beforeShowInst = inst; + return {currentText: "Current"}; +} + +function beforeDay(date) { + beforeShowDayThis = this; + beforeShowDayOK &= (date > new Date(2008, 1 - 1, 26) && + date < new Date(2008, 3 - 1, 6)); + return [(date.getDate() % 2 === 0), (date.getDate() % 10 === 0 ? "day10" : ""), + (date.getDate() % 3 === 0 ? "Divisble by 3" : "")]; +} + +test("callbacks", function() { + expect( 13 ); + // Before show + var dp, day20, day21, + inp = TestHelpers.datepicker.init("#inp", {beforeShow: beforeAll}), + inst = $.data(inp[0], "datepicker"); + equal($.datepicker._get(inst, "currentText"), "Today", "Before show - initial"); + inp.val("02/04/2008").datepicker("show"); + equal($.datepicker._get(inst, "currentText"), "Current", "Before show - changed"); + ok(beforeShowThis.id === inp[0].id, "Before show - this OK"); + ok(beforeShowInput.id === inp[0].id, "Before show - input OK"); + deepEqual(beforeShowInst, inst, "Before show - inst OK"); + inp.datepicker("hide").datepicker("destroy"); + // Before show day + inp = TestHelpers.datepicker.init("#inp", {beforeShowDay: beforeDay}); + dp = $("#ui-datepicker-div"); + inp.val("02/04/2008").datepicker("show"); + ok(beforeShowDayThis.id === inp[0].id, "Before show day - this OK"); + ok(beforeShowDayOK, "Before show day - dates OK"); + day20 = dp.find(".ui-datepicker-calendar td:contains('20')"); + day21 = dp.find(".ui-datepicker-calendar td:contains('21')"); + ok(!day20.is(".ui-datepicker-unselectable"), "Before show day - unselectable 20"); + ok(day21.is(".ui-datepicker-unselectable"), "Before show day - unselectable 21"); + ok(day20.is(".day10"), "Before show day - CSS 20"); + ok(!day21.is(".day10"), "Before show day - CSS 21"); + ok(!day20.attr("title"), "Before show day - title 20"); + ok(day21.attr("title") === "Divisble by 3", "Before show day - title 21"); + inp.datepicker("hide").datepicker("destroy"); +}); + +test("beforeShowDay - tooltips with quotes", function() { + expect( 1 ); + var inp, dp; + inp = TestHelpers.datepicker.init("#inp", { + beforeShowDay: function() { + return [ true, "", "'" ]; + } + }); + dp = $("#ui-datepicker-div"); + + inp.datepicker("show"); + equal( dp.find( ".ui-datepicker-calendar td:contains('9')").attr( "title" ), "'" ); + inp.datepicker("hide").datepicker("destroy"); +}); + +test("localisation", function() { + expect( 24 ); + var dp, month, day, date, + inp = TestHelpers.datepicker.init("#inp", $.datepicker.regional.fr); + inp.datepicker("option", {dateFormat: "DD, d MM yy", showButtonPanel:true, changeMonth:true, changeYear:true}).val("").datepicker("show"); + dp = $("#ui-datepicker-div"); + equal($(".ui-datepicker-close", dp).text(), "Fermer", "Localisation - close"); + $(".ui-datepicker-close", dp).simulate("mouseover"); + equal($(".ui-datepicker-prev", dp).text(), "Précédent", "Localisation - previous"); + equal($(".ui-datepicker-current", dp).text(), "Aujourd'hui", "Localisation - current"); + equal($(".ui-datepicker-next", dp).text(), "Suivant", "Localisation - next"); + month = 0; + $(".ui-datepicker-month option", dp).each(function() { + equal($(this).text(), $.datepicker.regional.fr.monthNamesShort[month], + "Localisation - month " + month); + month++; + }); + day = 1; + $(".ui-datepicker-calendar th", dp).each(function() { + equal($(this).text(), $.datepicker.regional.fr.dayNamesMin[day], + "Localisation - day " + day); + day = (day + 1) % 7; + }); + inp.simulate("keydown", {keyCode: $.ui.keyCode.ENTER}); + date = new Date(); + equal(inp.val(), $.datepicker.regional.fr.dayNames[date.getDay()] + ", " + + date.getDate() + " " + $.datepicker.regional.fr.monthNames[date.getMonth()] + + " " + date.getFullYear(), "Localisation - formatting"); +}); + +test("noWeekends", function() { + expect( 31 ); + var i, date; + for (i = 1; i <= 31; i++) { + date = new Date(2001, 1 - 1, i); + deepEqual($.datepicker.noWeekends(date), [(i + 1) % 7 >= 2, ""], + "No weekends " + date); + } +}); + +test("iso8601Week", function() { + expect( 12 ); + var date = new Date(2000, 12 - 1, 31); + equal($.datepicker.iso8601Week(date), 52, "ISO 8601 week " + date); + date = new Date(2001, 1 - 1, 1); + equal($.datepicker.iso8601Week(date), 1, "ISO 8601 week " + date); + date = new Date(2001, 1 - 1, 7); + equal($.datepicker.iso8601Week(date), 1, "ISO 8601 week " + date); + date = new Date(2001, 1 - 1, 8); + equal($.datepicker.iso8601Week(date), 2, "ISO 8601 week " + date); + date = new Date(2003, 12 - 1, 28); + equal($.datepicker.iso8601Week(date), 52, "ISO 8601 week " + date); + date = new Date(2003, 12 - 1, 29); + equal($.datepicker.iso8601Week(date), 1, "ISO 8601 week " + date); + date = new Date(2004, 1 - 1, 4); + equal($.datepicker.iso8601Week(date), 1, "ISO 8601 week " + date); + date = new Date(2004, 1 - 1, 5); + equal($.datepicker.iso8601Week(date), 2, "ISO 8601 week " + date); + date = new Date(2009, 12 - 1, 28); + equal($.datepicker.iso8601Week(date), 53, "ISO 8601 week " + date); + date = new Date(2010, 1 - 1, 3); + equal($.datepicker.iso8601Week(date), 53, "ISO 8601 week " + date); + date = new Date(2010, 1 - 1, 4); + equal($.datepicker.iso8601Week(date), 1, "ISO 8601 week " + date); + date = new Date(2010, 1 - 1, 10); + equal($.datepicker.iso8601Week(date), 1, "ISO 8601 week " + date); +}); + +test("parseDate", function() { + expect( 26 ); + TestHelpers.datepicker.init("#inp"); + var currentYear, gmtDate, fr, settings, zh; + ok($.datepicker.parseDate("d m y", "") == null, "Parse date empty"); + TestHelpers.datepicker.equalsDate($.datepicker.parseDate("d m y", "3 2 01"), + new Date(2001, 2 - 1, 3), "Parse date d m y"); + TestHelpers.datepicker.equalsDate($.datepicker.parseDate("dd mm yy", "03 02 2001"), + new Date(2001, 2 - 1, 3), "Parse date dd mm yy"); + TestHelpers.datepicker.equalsDate($.datepicker.parseDate("d m y", "13 12 01"), + new Date(2001, 12 - 1, 13), "Parse date d m y"); + TestHelpers.datepicker.equalsDate($.datepicker.parseDate("dd mm yy", "13 12 2001"), + new Date(2001, 12 - 1, 13), "Parse date dd mm yy"); + TestHelpers.datepicker.equalsDate($.datepicker.parseDate("y-o", "01-34"), + new Date(2001, 2 - 1, 3), "Parse date y-o"); + TestHelpers.datepicker.equalsDate($.datepicker.parseDate("yy-oo", "2001-347"), + new Date(2001, 12 - 1, 13), "Parse date yy-oo"); + TestHelpers.datepicker.equalsDate($.datepicker.parseDate("oo yy", "348 2004"), + new Date(2004, 12 - 1, 13), "Parse date oo yy"); + TestHelpers.datepicker.equalsDate($.datepicker.parseDate("D d M y", "Sat 3 Feb 01"), + new Date(2001, 2 - 1, 3), "Parse date D d M y"); + TestHelpers.datepicker.equalsDate($.datepicker.parseDate("d MM DD yy", "3 February Saturday 2001"), + new Date(2001, 2 - 1, 3), "Parse date dd MM DD yy"); + TestHelpers.datepicker.equalsDate($.datepicker.parseDate("DD, MM d, yy", "Saturday, February 3, 2001"), + new Date(2001, 2 - 1, 3), "Parse date DD, MM d, yy"); + TestHelpers.datepicker.equalsDate($.datepicker.parseDate("'day' d 'of' MM (''DD''), yy", + "day 3 of February ('Saturday'), 2001"), new Date(2001, 2 - 1, 3), + "Parse date 'day' d 'of' MM (''DD''), yy"); + currentYear = new Date().getFullYear(); + TestHelpers.datepicker.equalsDate($.datepicker.parseDate("y-m-d", (currentYear - 2000) + "-02-03"), + new Date(currentYear, 2 - 1, 3), "Parse date y-m-d - default cutuff"); + TestHelpers.datepicker.equalsDate($.datepicker.parseDate("y-m-d", (currentYear - 2000 + 10) + "-02-03"), + new Date(currentYear+10, 2 - 1, 3), "Parse date y-m-d - default cutuff"); + TestHelpers.datepicker.equalsDate($.datepicker.parseDate("y-m-d", (currentYear - 2000 + 11) + "-02-03"), + new Date(currentYear-89, 2 - 1, 3), "Parse date y-m-d - default cutuff"); + TestHelpers.datepicker.equalsDate($.datepicker.parseDate("y-m-d", "80-02-03", {shortYearCutoff: 80}), + new Date(2080, 2 - 1, 3), "Parse date y-m-d - cutoff 80"); + TestHelpers.datepicker.equalsDate($.datepicker.parseDate("y-m-d", "81-02-03", {shortYearCutoff: 80}), + new Date(1981, 2 - 1, 3), "Parse date y-m-d - cutoff 80"); + TestHelpers.datepicker.equalsDate($.datepicker.parseDate("y-m-d", (currentYear - 2000 + 60) + "-02-03", {shortYearCutoff: "+60"}), + new Date(currentYear + 60, 2 - 1, 3), "Parse date y-m-d - cutoff +60"); + TestHelpers.datepicker.equalsDate($.datepicker.parseDate("y-m-d", (currentYear - 2000 + 61) + "-02-03", {shortYearCutoff: "+60"}), + new Date(currentYear - 39, 2 - 1, 3), "Parse date y-m-d - cutoff +60"); + gmtDate = new Date(2001, 2 - 1, 3); + gmtDate.setMinutes(gmtDate.getMinutes() - gmtDate.getTimezoneOffset()); + TestHelpers.datepicker.equalsDate($.datepicker.parseDate("@", "981158400000"), gmtDate, "Parse date @"); + TestHelpers.datepicker.equalsDate($.datepicker.parseDate("!", "631167552000000000"), gmtDate, "Parse date !"); + + fr = $.datepicker.regional.fr; + settings = {dayNamesShort: fr.dayNamesShort, dayNames: fr.dayNames, + monthNamesShort: fr.monthNamesShort, monthNames: fr.monthNames}; + TestHelpers.datepicker.equalsDate($.datepicker.parseDate("D d M y", "Lun. 9 Avril 01", settings), + new Date(2001, 4 - 1, 9), "Parse date D M y with settings"); + TestHelpers.datepicker.equalsDate($.datepicker.parseDate("d MM DD yy", "9 Avril Lundi 2001", settings), + new Date(2001, 4 - 1, 9), "Parse date d MM DD yy with settings"); + TestHelpers.datepicker.equalsDate($.datepicker.parseDate("DD, MM d, yy", "Lundi, Avril 9, 2001", settings), + new Date(2001, 4 - 1, 9), "Parse date DD, MM d, yy with settings"); + TestHelpers.datepicker.equalsDate($.datepicker.parseDate("'jour' d 'de' MM (''DD''), yy", "jour 9 de Avril ('Lundi'), 2001", settings), + new Date(2001, 4 - 1, 9), "Parse date 'jour' d 'de' MM (''DD''), yy with settings"); + + zh = $.datepicker.regional["zh-CN"]; + TestHelpers.datepicker.equalsDate($.datepicker.parseDate("yy M d", "2011 十一月 22", zh), + new Date(2011, 11 - 1, 22), "Parse date yy M d with zh-CN"); +}); + +test("parseDateErrors", function() { + expect( 17 ); + TestHelpers.datepicker.init("#inp"); + var fr, settings; + function expectError(expr, value, error) { + try { + expr(); + ok(false, "Parsed error " + value); + } + catch (e) { + equal(e, error, "Parsed error " + value); + } + } + expectError(function() { $.datepicker.parseDate(null, "Sat 2 01"); }, + "Sat 2 01", "Invalid arguments"); + expectError(function() { $.datepicker.parseDate("d m y", null); }, + "null", "Invalid arguments"); + expectError(function() { $.datepicker.parseDate("d m y", "Sat 2 01"); }, + "Sat 2 01 - d m y", "Missing number at position 0"); + expectError(function() { $.datepicker.parseDate("dd mm yy", "Sat 2 01"); }, + "Sat 2 01 - dd mm yy", "Missing number at position 0"); + expectError(function() { $.datepicker.parseDate("d m y", "3 Feb 01"); }, + "3 Feb 01 - d m y", "Missing number at position 2"); + expectError(function() { $.datepicker.parseDate("dd mm yy", "3 Feb 01"); }, + "3 Feb 01 - dd mm yy", "Missing number at position 2"); + expectError(function() { $.datepicker.parseDate("d m y", "3 2 AD01"); }, + "3 2 AD01 - d m y", "Missing number at position 4"); + expectError(function() { $.datepicker.parseDate("d m yy", "3 2 AD01"); }, + "3 2 AD01 - dd mm yy", "Missing number at position 4"); + expectError(function() { $.datepicker.parseDate("y-o", "01-D01"); }, + "2001-D01 - y-o", "Missing number at position 3"); + expectError(function() { $.datepicker.parseDate("yy-oo", "2001-D01"); }, + "2001-D01 - yy-oo", "Missing number at position 5"); + expectError(function() { $.datepicker.parseDate("D d M y", "D7 3 Feb 01"); }, + "D7 3 Feb 01 - D d M y", "Unknown name at position 0"); + expectError(function() { $.datepicker.parseDate("D d M y", "Sat 3 M2 01"); }, + "Sat 3 M2 01 - D d M y", "Unknown name at position 6"); + expectError(function() { $.datepicker.parseDate("DD, MM d, yy", "Saturday- Feb 3, 2001"); }, + "Saturday- Feb 3, 2001 - DD, MM d, yy", "Unexpected literal at position 8"); + expectError(function() { $.datepicker.parseDate("'day' d 'of' MM (''DD''), yy", + "day 3 of February (\"Saturday\"), 2001"); }, + "day 3 of Mon2 ('Day7'), 2001", "Unexpected literal at position 19"); + expectError(function() { $.datepicker.parseDate("d m y", "29 2 01"); }, + "29 2 01 - d m y", "Invalid date"); + fr = $.datepicker.regional.fr; + settings = {dayNamesShort: fr.dayNamesShort, dayNames: fr.dayNames, + monthNamesShort: fr.monthNamesShort, monthNames: fr.monthNames}; + expectError(function() { $.datepicker.parseDate("D d M y", "Mon 9 Avr 01", settings); }, + "Mon 9 Avr 01 - D d M y", "Unknown name at position 0"); + expectError(function() { $.datepicker.parseDate("D d M y", "Lun. 9 Apr 01", settings); }, + "Lun. 9 Apr 01 - D d M y", "Unknown name at position 7"); +}); + +test("Ticket #7244: date parser does not fail when too many numbers are passed into the date function", function() { + expect( 4 ); + var date; + try{ + date = $.datepicker.parseDate("dd/mm/yy", "18/04/19881"); + ok(false, "Did not properly detect an invalid date"); + }catch(e){ + ok("invalid date detected"); + } + + try { + date = $.datepicker.parseDate("dd/mm/yy", "18/04/1988 @ 2:43 pm"); + equal(date.getDate(), 18); + equal(date.getMonth(), 3); + equal(date.getFullYear(), 1988); + } catch(e) { + ok(false, "Did not properly parse date with extra text separated by whitespace"); + } +}); + +test("formatDate", function() { + expect( 16 ); + TestHelpers.datepicker.init("#inp"); + var gmtDate, fr, settings; + equal($.datepicker.formatDate("d m y", new Date(2001, 2 - 1, 3)), + "3 2 01", "Format date d m y"); + equal($.datepicker.formatDate("dd mm yy", new Date(2001, 2 - 1, 3)), + "03 02 2001", "Format date dd mm yy"); + equal($.datepicker.formatDate("d m y", new Date(2001, 12 - 1, 13)), + "13 12 01", "Format date d m y"); + equal($.datepicker.formatDate("dd mm yy", new Date(2001, 12 - 1, 13)), + "13 12 2001", "Format date dd mm yy"); + equal($.datepicker.formatDate("yy-o", new Date(2001, 2 - 1, 3)), + "2001-34", "Format date yy-o"); + equal($.datepicker.formatDate("yy-oo", new Date(2001, 2 - 1, 3)), + "2001-034", "Format date yy-oo"); + equal($.datepicker.formatDate("D M y", new Date(2001, 2 - 1, 3)), + "Sat Feb 01", "Format date D M y"); + equal($.datepicker.formatDate("DD MM yy", new Date(2001, 2 - 1, 3)), + "Saturday February 2001", "Format date DD MM yy"); + equal($.datepicker.formatDate("DD, MM d, yy", new Date(2001, 2 - 1, 3)), + "Saturday, February 3, 2001", "Format date DD, MM d, yy"); + equal($.datepicker.formatDate("'day' d 'of' MM (''DD''), yy", + new Date(2001, 2 - 1, 3)), "day 3 of February ('Saturday'), 2001", + "Format date 'day' d 'of' MM ('DD'), yy"); + gmtDate = new Date(2001, 2 - 1, 3); + gmtDate.setMinutes(gmtDate.getMinutes() - gmtDate.getTimezoneOffset()); + equal($.datepicker.formatDate("@", gmtDate), "981158400000", "Format date @"); + equal($.datepicker.formatDate("!", gmtDate), "631167552000000000", "Format date !"); + fr = $.datepicker.regional.fr; + settings = {dayNamesShort: fr.dayNamesShort, dayNames: fr.dayNames, + monthNamesShort: fr.monthNamesShort, monthNames: fr.monthNames}; + equal($.datepicker.formatDate("D M y", new Date(2001, 4 - 1, 9), settings), + "lun. avril 01", "Format date D M y with settings"); + equal($.datepicker.formatDate("DD MM yy", new Date(2001, 4 - 1, 9), settings), + "lundi avril 2001", "Format date DD MM yy with settings"); + equal($.datepicker.formatDate("DD, MM d, yy", new Date(2001, 4 - 1, 9), settings), + "lundi, avril 9, 2001", "Format date DD, MM d, yy with settings"); + equal($.datepicker.formatDate("'jour' d 'de' MM (''DD''), yy", + new Date(2001, 4 - 1, 9), settings), "jour 9 de avril ('lundi'), 2001", + "Format date 'jour' d 'de' MM (''DD''), yy with settings"); +}); + +// TODO: Fix this test so it isn't mysteriously flaky in Browserstack on certain OS/Browser combos +// test("Ticket 6827: formatDate day of year calculation is wrong during day lights savings time", function(){ +// expect( 1 ); +// var time = $.datepicker.formatDate("oo", new Date("2010/03/30 12:00:00 CDT")); +// equal(time, "089"); +// }); + +test( "Ticket 7602: Stop datepicker from appearing with beforeShow event handler", function() { + expect( 3 ); + + var inp, dp; + + inp = TestHelpers.datepicker.init( "#inp", { + beforeShow: function() { + } + }); + dp = $( "#ui-datepicker-div" ); + inp.datepicker( "show" ); + equal( dp.css( "display" ), "block", "beforeShow returns nothing" ); + inp.datepicker( "hide" ).datepicker( "destroy" ); + + inp = TestHelpers.datepicker.init( "#inp", { + beforeShow: function() { + return true; + } + }); + dp = $( "#ui-datepicker-div" ); + inp.datepicker( "show" ); + equal( dp.css( "display" ), "block", "beforeShow returns true" ); + inp.datepicker( "hide" ); + inp.datepicker( "destroy" ); + + inp = TestHelpers.datepicker.init( "#inp", { + beforeShow: function() { + return false; + } + }); + dp = $( "#ui-datepicker-div" ); + inp.datepicker( "show" ); + equal( dp.css( "display" ), "none","beforeShow returns false" ); + inp.datepicker( "destroy" ); +}); + +})(jQuery); diff --git a/apps/it/static/js/ui/tests/unit/datepicker/datepicker_test_helpers.js b/apps/it/static/js/ui/tests/unit/datepicker/datepicker_test_helpers.js new file mode 100644 index 0000000..9cb63c9 --- /dev/null +++ b/apps/it/static/js/ui/tests/unit/datepicker/datepicker_test_helpers.js @@ -0,0 +1,37 @@ +TestHelpers.datepicker = { + addMonths: function(date, offset) { + var maxDay = 32 - new Date(date.getFullYear(), date.getMonth() + offset, 32).getDate(); + date.setDate(Math.min(date.getDate(), maxDay)); + date.setMonth(date.getMonth() + offset); + return date; + }, + equalsDate: function(d1, d2, message) { + if (!d1 || !d2) { + ok(false, message + " - missing date"); + return; + } + d1 = new Date(d1.getFullYear(), d1.getMonth(), d1.getDate()); + d2 = new Date(d2.getFullYear(), d2.getMonth(), d2.getDate()); + equal(d1.toString(), d2.toString(), message); + }, + init: function( id, options ) { + $.datepicker.setDefaults( $.datepicker.regional[ "" ] ); + return $( id ).datepicker( $.extend( { showAnim: "" }, options || {} ) ); + }, + initNewInput: function( options ) { + var id = $( "<input>" ).appendTo( "#qunit-fixture" ); + return TestHelpers.datepicker.init( id, options ); + }, + onFocus: function( element, onFocus ) { + var fn = function( event ){ + if( !event.originalEvent ) { + return; + } + element.unbind( "focus", fn ); + onFocus(); + }; + + element.bind( "focus", fn )[ 0 ].focus(); + }, + PROP_NAME: "datepicker" +};
\ No newline at end of file diff --git a/apps/it/static/js/ui/tests/unit/datepicker/images/calendar.gif b/apps/it/static/js/ui/tests/unit/datepicker/images/calendar.gif Binary files differnew file mode 100644 index 0000000..d0abaa7 --- /dev/null +++ b/apps/it/static/js/ui/tests/unit/datepicker/images/calendar.gif diff --git a/apps/it/static/js/ui/tests/unit/dialog/all.html b/apps/it/static/js/ui/tests/unit/dialog/all.html new file mode 100644 index 0000000..9efbe3e --- /dev/null +++ b/apps/it/static/js/ui/tests/unit/dialog/all.html @@ -0,0 +1,30 @@ +<!doctype html> +<html lang="en"> +<head> + <meta charset="utf-8"> + <title>jQuery UI Dialog Test Suite</title> + + <script src="../../../jquery-1.10.2.js"></script> + + <link rel="stylesheet" href="../../../external/qunit.css"> + <link rel="stylesheet" href="../qunit-composite.css"> + <script src="../../../external/qunit.js"></script> + <script src="../qunit-composite.js"></script> + <script src="../subsuite.js"></script> + + <script> + testAllVersions( "dialog" ); + </script> +</head> +<body> + +<h1 id="qunit-header">jQuery UI Dialog Test Suite</h1> +<h2 id="qunit-banner"></h2> +<div id="qunit-testrunner-toolbar"></div> +<h2 id="qunit-userAgent"></h2> +<ol id="qunit-tests"></ol> +<div id="qunit-fixture"> + +</div> +</body> +</html> diff --git a/apps/it/static/js/ui/tests/unit/dialog/dialog.html b/apps/it/static/js/ui/tests/unit/dialog/dialog.html new file mode 100644 index 0000000..d8506a1 --- /dev/null +++ b/apps/it/static/js/ui/tests/unit/dialog/dialog.html @@ -0,0 +1,71 @@ +<!doctype html> +<html lang="en"> +<head> + <meta charset="utf-8"> + <title>jQuery UI Dialog Test Suite</title> + + <script src="../../jquery.js"></script> + <script> + $.uiBackCompat = false; + </script> + <link rel="stylesheet" href="../../../external/qunit.css"> + <script src="../../../external/qunit.js"></script> + <script src="../../jquery.simulate.js"></script> + <script src="../testsuite.js"></script> + <script> + TestHelpers.loadResources({ + css: [ "ui.core", "ui.dialog" ], + js: [ + "ui/jquery.ui.core.js", + "ui/jquery.ui.widget.js", + "ui/jquery.ui.position.js", + "ui/jquery.ui.mouse.js", + "ui/jquery.ui.draggable.js", + "ui/jquery.ui.resizable.js", + "ui/jquery.ui.button.js", + "ui/jquery.ui.effect.js", + "ui/jquery.ui.effect-blind.js", + "ui/jquery.ui.effect-clip.js", + "ui/jquery.ui.effect-explode.js", + "ui/jquery.ui.dialog.js" + ] + }); + </script> + + <script src="dialog_common.js"></script> + <script src="dialog_core.js"></script> + <script src="dialog_events.js"></script> + <script src="dialog_methods.js"></script> + <script src="dialog_options.js"></script> + <script src="dialog_test_helpers.js"></script> + + <script src="../swarminject.js"></script> +</head> +<body> + +<h1 id="qunit-header">jQuery UI Dialog Test Suite</h1> +<h2 id="qunit-banner"></h2> +<div id="qunit-testrunner-toolbar"></div> +<h2 id="qunit-userAgent"></h2> +<ol id="qunit-tests"></ol> +<div id="qunit-fixture"> + <div id="dialog1"></div> + <div id="dialog2"></div> + <div id="form-dialog" title="Profile Information"> + <!-- create a spacer to ensure there's enough space to scroll --> + <div style="height: 250px;">...</div> + <fieldset> + <legend>Please share some personal information</legend> + <label for="favorite-animal">Your favorite animal</label><input id="favorite-animal"> + <label for="favorite-color">Your favorite color</label><input id="favorite-color"> + </fieldset> + <div role="group" aria-describedby="section2"> + <p id="section2">Some more (optional) information</p> + <label for="favorite-food">Favorite food</label><input id="favorite-food"> + </div> + </div> + <div class="wrap" id="wrap1"></div> + <div class="wrap" id="wrap2"></div> +</div> +</body> +</html> diff --git a/apps/it/static/js/ui/tests/unit/dialog/dialog_common.js b/apps/it/static/js/ui/tests/unit/dialog/dialog_common.js new file mode 100644 index 0000000..ea4c917 --- /dev/null +++ b/apps/it/static/js/ui/tests/unit/dialog/dialog_common.js @@ -0,0 +1,43 @@ +TestHelpers.commonWidgetTests( "dialog", { + defaults: { + appendTo: "body", + autoOpen: true, + buttons: [], + closeOnEscape: true, + closeText: "close", + disabled: false, + dialogClass: "", + draggable: true, + height: "auto", + hide: null, + maxHeight: null, + maxWidth: null, + minHeight: 150, + minWidth: 150, + modal: false, + position: { + my: "center", + at: "center", + of: window, + collision: "fit", + using: $.ui.dialog.prototype.options.position.using + }, + resizable: true, + show: null, + title: null, + width: 300, + + // callbacks + beforeClose: null, + close: null, + create: null, + drag: null, + dragStart: null, + dragStop: null, + focus: null, + open: null, + resize: null, + resizeStart: null, + resizeStop: null + } +}); diff --git a/apps/it/static/js/ui/tests/unit/dialog/dialog_core.js b/apps/it/static/js/ui/tests/unit/dialog/dialog_core.js new file mode 100644 index 0000000..e85759d --- /dev/null +++ b/apps/it/static/js/ui/tests/unit/dialog/dialog_core.js @@ -0,0 +1,163 @@ +/* + * dialog_core.js + */ + +(function($) { + +module("dialog: core"); + +test("title id", function() { + expect(1); + + var titleId, + element = $("<div></div>").dialog(); + + titleId = element.dialog("widget").find(".ui-dialog-title").attr("id"); + ok( /ui-id-\d+$/.test( titleId ), "auto-numbered title id"); + element.remove(); +}); + +test( "ARIA", function() { + expect( 4 ); + + var element = $( "<div></div>" ).dialog(), + wrapper = element.dialog( "widget" ); + equal( wrapper.attr( "role" ), "dialog", "dialog role" ); + equal( wrapper.attr( "aria-labelledby" ), wrapper.find( ".ui-dialog-title" ).attr( "id" ) ); + equal( wrapper.attr( "aria-describedby" ), element.attr( "id" ), "aria-describedby added" ); + element.remove(); + + element = $("<div><div aria-describedby='section2'><p id='section2'>descriotion</p></div></div>").dialog(); + strictEqual( element.dialog( "widget" ).attr( "aria-describedby" ), undefined, "no aria-describedby added, as already present in markup" ); + element.remove(); +}); + +test("widget method", function() { + expect( 1 ); + var dialog = $("<div>").appendTo("#qunit-fixture").dialog(); + deepEqual(dialog.parent()[0], dialog.dialog("widget")[0]); + dialog.remove(); +}); + +asyncTest( "focus tabbable", function() { + expect( 5 ); + var element, + options = { + buttons: [{ + text: "Ok", + click: $.noop + }] + }; + + function checkFocus( markup, options, testFn, next ) { + element = $( markup ).dialog( options ); + setTimeout(function() { + testFn(); + element.remove(); + setTimeout( next ); + }); + } + + function step1() { + checkFocus( "<div><input><input autofocus></div>", options, function() { + equal( document.activeElement, element.find( "input" )[ 1 ], + "1. first element inside the dialog matching [autofocus]" ); + }, step2 ); + } + + function step2() { + checkFocus( "<div><input><input></div>", options, function() { + equal( document.activeElement, element.find( "input" )[ 0 ], + "2. tabbable element inside the content element" ); + }, step3 ); + } + + function step3() { + checkFocus( "<div>text</div>", options, function() { + equal( document.activeElement, + element.dialog( "widget" ).find( ".ui-dialog-buttonpane button" )[ 0 ], + "3. tabbable element inside the buttonpane" ); + }, step4 ); + } + + function step4() { + checkFocus( "<div>text</div>", {}, function() { + equal( document.activeElement, + element.dialog( "widget" ).find( ".ui-dialog-titlebar .ui-dialog-titlebar-close" )[ 0 ], + "4. the close button" ); + }, step5 ); + } + + function step5() { + element = $( "<div>text</div>" ).dialog({ + autoOpen: false + }); + element.dialog( "widget" ).find( ".ui-dialog-titlebar-close" ).hide(); + element.dialog( "open" ); + setTimeout(function() { + equal( document.activeElement, element.parent()[ 0 ], "5. the dialog itself" ); + element.remove(); + start(); + }); + } + + step1(); +}); + +test( "#7960: resizable handles below modal overlays", function() { + expect( 1 ); + + var resizable = $( "<div>" ).resizable(), + dialog = $( "<div>" ).dialog({ modal: true }), + resizableZindex = parseInt( resizable.find( ".ui-resizable-handle" ).css( "zIndex" ), 10 ), + overlayZindex = parseInt( $( ".ui-widget-overlay" ).css( "zIndex" ), 10 ); + + ok( resizableZindex < overlayZindex, "Resizable handles have lower z-index than modal overlay" ); + dialog.dialog( "destroy" ); +}); + +asyncTest( "Prevent tabbing out of dialogs", function() { + expect( 3 ); + + var element = $( "<div><input><input></div>" ).dialog(), + inputs = element.find( "input" ), + widget = element.dialog( "widget" )[ 0 ]; + + function checkTab() { + ok( $.contains( widget, document.activeElement ), "Tab key event moved focus within the modal" ); + + // check shift tab + $( document.activeElement ).simulate( "keydown", { keyCode: $.ui.keyCode.TAB, shiftKey: true }); + setTimeout( checkShiftTab ); + } + + function checkShiftTab() { + ok( $.contains( widget, document.activeElement ), "Shift-Tab key event moved focus within the modal" ); + + element.remove(); + setTimeout( start ); + } + + inputs[1].focus(); + setTimeout(function() { + equal( document.activeElement, inputs[1], "Focus set on second input" ); + inputs.eq( 1 ).simulate( "keydown", { keyCode: $.ui.keyCode.TAB }); + + setTimeout( checkTab ); + }); +}); + +asyncTest( "#9048: multiple modal dialogs opened and closed in different order", function() { + expect( 1 ); + $( "#dialog1, #dialog2" ).dialog({ autoOpen: false, modal:true }); + $( "#dialog1" ).dialog( "open" ); + $( "#dialog2" ).dialog( "open" ); + $( "#dialog1" ).dialog( "close" ); + setTimeout(function() { + $( "#dialog2" ).dialog( "close" ); + $( "#favorite-animal" ).focus(); + ok( true, "event handlers cleaned up (no errors thrown)" ); + start(); + }); +}); +})(jQuery); diff --git a/apps/it/static/js/ui/tests/unit/dialog/dialog_deprecated.html b/apps/it/static/js/ui/tests/unit/dialog/dialog_deprecated.html new file mode 100644 index 0000000..1f8bac6 --- /dev/null +++ b/apps/it/static/js/ui/tests/unit/dialog/dialog_deprecated.html @@ -0,0 +1,67 @@ +<!doctype html> +<html lang="en"> +<head> + <meta charset="utf-8"> + <title>jQuery UI Dialog Test Suite</title> + + <script src="../../jquery.js"></script> + <link rel="stylesheet" href="../../../external/qunit.css"> + <script src="../../../external/qunit.js"></script> + <script src="../../jquery.simulate.js"></script> + <script src="../testsuite.js"></script> + <script> + TestHelpers.loadResources({ + css: [ "ui.core", "ui.dialog" ], + js: [ + "ui/jquery.ui.core.js", + "ui/jquery.ui.widget.js", + "ui/jquery.ui.position.js", + "ui/jquery.ui.mouse.js", + "ui/jquery.ui.draggable.js", + "ui/jquery.ui.resizable.js", + "ui/jquery.ui.button.js", + "ui/jquery.ui.effect.js", + "ui/jquery.ui.effect-blind.js", + "ui/jquery.ui.effect-clip.js", + "ui/jquery.ui.effect-explode.js", + "ui/jquery.ui.dialog.js" + ] + }); + </script> + + <script src="dialog_common.js"></script> + <script src="dialog_core.js"></script> + <script src="dialog_events.js"></script> + <script src="dialog_methods.js"></script> + <script src="dialog_options.js"></script> + <script src="dialog_test_helpers.js"></script> + <script src="dialog_deprecated.js"></script> + + <script src="../swarminject.js"></script> +</head> +<body> + +<h1 id="qunit-header">jQuery UI Dialog Test Suite</h1> +<h2 id="qunit-banner"></h2> +<div id="qunit-testrunner-toolbar"></div> +<h2 id="qunit-userAgent"></h2> +<ol id="qunit-tests"></ol> +<div id="qunit-fixture"> + <div id="dialog1"></div> + <div id="dialog2"></div> + <div id="form-dialog" title="Profile Information"> + <fieldset> + <legend>Please share some personal information</legend> + <label for="favorite-animal">Your favorite animal</label><input id="favorite-animal"> + <label for="favorite-color">Your favorite color</label><input id="favorite-color"> + </fieldset> + <div role="group" aria-describedby="section2"> + <p id="section2">Some more (optional) information</p> + <label for="favorite-food">Favorite food</label><input id="favorite-food"> + </div> + </div> + <div class="wrap" id="wrap1"></div> + <div class="wrap" id="wrap2"></div> +</div> +</body> +</html> diff --git a/apps/it/static/js/ui/tests/unit/dialog/dialog_deprecated.js b/apps/it/static/js/ui/tests/unit/dialog/dialog_deprecated.js new file mode 100644 index 0000000..ff6284e --- /dev/null +++ b/apps/it/static/js/ui/tests/unit/dialog/dialog_deprecated.js @@ -0,0 +1,63 @@ +module("dialog (deprecated): position option with string and array"); + +test( "position, right bottom on window w/array", function() { + expect( 2 ); + + // dialogs alter the window width and height in FF and IE7 + // so we collect that information before creating the dialog + // Support: FF, IE7 + var winWidth = $( window ).width(), + winHeight = $( window ).height(), + element = $("<div></div>").dialog({ position: [ "right", "bottom" ] }), + dialog = element.dialog("widget"), + offset = dialog.offset(); + closeEnough( offset.left, winWidth - dialog.outerWidth() + $( window ).scrollLeft(), 1, "offset left of right bottom on window w/array" ); + closeEnough( offset.top, winHeight - dialog.outerHeight() + $( window ).scrollTop(), 1, "offset top of right bottom on window w/array" ); + element.remove(); +}); + +test( "position, right bottom on window", function() { + expect( 2 ); + + // dialogs alter the window width and height in FF and IE7 + // so we collect that information before creating the dialog + // Support: FF, IE7 + var winWidth = $( window ).width(), + winHeight = $( window ).height(), + element = $("<div></div>").dialog({ position: "right bottom" }), + dialog = element.dialog("widget"), + offset = dialog.offset(); + closeEnough( offset.left, winWidth - dialog.outerWidth() + $( window ).scrollLeft(), 1, "offset left of right bottom on window" ); + closeEnough( offset.top, winHeight - dialog.outerHeight() + $( window ).scrollTop(), 1, "offset top of right bottom on window" ); + element.remove(); +}); + +test("position, offset from top left w/array", function() { + expect( 2 ); + var element = $("<div></div>").dialog({ position: [10, 10] }), + dialog = element.dialog("widget"), + offset = dialog.offset(); + closeEnough(offset.left, 10 + $(window).scrollLeft(), 1); + closeEnough(offset.top, 10 + $(window).scrollTop(), 1); + element.remove(); +}); + +test("position, top on window", function() { + expect( 2 ); + var element = $("<div></div>").dialog({ position: "top" }), + dialog = element.dialog("widget"), + offset = dialog.offset(); + closeEnough(offset.left, Math.round($(window).width() / 2 - dialog.outerWidth() / 2) + $(window).scrollLeft(), 1); + closeEnough(offset.top, $(window).scrollTop(), 1); + element.remove(); +}); + +test("position, left on window", function() { + expect( 2 ); + var element = $("<div></div>").dialog({ position: "left" }), + dialog = element.dialog("widget"), + offset = dialog.offset(); + closeEnough(offset.left, 0, 1); + closeEnough(offset.top, Math.round($(window).height() / 2 - dialog.outerHeight() / 2) + $(window).scrollTop(), 1); + element.remove(); +}); diff --git a/apps/it/static/js/ui/tests/unit/dialog/dialog_events.js b/apps/it/static/js/ui/tests/unit/dialog/dialog_events.js new file mode 100644 index 0000000..7bcd567 --- /dev/null +++ b/apps/it/static/js/ui/tests/unit/dialog/dialog_events.js @@ -0,0 +1,370 @@ +/* + * dialog_events.js + */ +(function($) { + +module("dialog: events"); + +test("open", function() { + expect(13); + + var element = $("<div></div>"); + element.dialog({ + open: function(ev, ui) { + ok(element.data("ui-dialog")._isOpen, "interal _isOpen flag is set"); + ok(true, "autoOpen: true fires open callback"); + equal(this, element[0], "context of callback"); + equal(ev.type, "dialogopen", "event type in callback"); + deepEqual(ui, {}, "ui hash in callback"); + } + }); + element.remove(); + + element = $("<div></div>"); + element.dialog({ + autoOpen: false, + open: function(ev, ui) { + ok(true, ".dialog('open') fires open callback"); + equal(this, element[0], "context of callback"); + equal(ev.type, "dialogopen", "event type in callback"); + deepEqual(ui, {}, "ui hash in callback"); + } + }).bind("dialogopen", function(ev, ui) { + ok(element.data("ui-dialog")._isOpen, "interal _isOpen flag is set"); + ok(true, "dialog('open') fires open event"); + equal(this, element[0], "context of event"); + deepEqual(ui, {}, "ui hash in event"); + }); + element.dialog("open"); + element.remove(); +}); + + +test( "focus", function() { + expect( 5 ); + var element, other; + element = $("#dialog1").dialog({ + autoOpen: false + }); + other = $("#dialog2").dialog({ + autoOpen: false + }); + + element.one( "dialogopen", function() { + ok( true, "open, just once" ); + }); + element.one( "dialogfocus", function() { + ok( true, "focus on open" ); + }); + other.dialog( "open" ); + + element.one( "dialogfocus", function() { + ok( true, "when opening and already open and wasn't on top" ); + }); + other.dialog( "open" ); + element.dialog( "open" ); + + element.one( "dialogfocus", function() { + ok( true, "when calling moveToTop and wasn't on top" ); + }); + other.dialog( "moveToTop" ); + element.dialog( "moveToTop" ); + + element.bind( "dialogfocus", function() { + ok( true, "when mousedown anywhere on the dialog and it wasn't on top" ); + }); + other.dialog( "moveToTop" ); + element.trigger( "mousedown" ); + + // triggers just once when already on top + element.dialog( "open" ); + element.dialog( "moveToTop" ); + element.trigger( "mousedown" ); + + element.add( other ).remove(); +}); + +test("dragStart", function() { + expect(9); + + var handle, + element = $("<div></div>").dialog({ + dragStart: function(ev, ui) { + ok(true, "dragging fires dragStart callback"); + equal(this, element[0], "context of callback"); + equal(ev.type, "dialogdragstart", "event type in callback"); + ok(ui.position !== undefined, "ui.position in callback"); + ok(ui.offset !== undefined, "ui.offset in callback"); + } + }).bind("dialogdragstart", function(ev, ui) { + ok(true, "dragging fires dialogdragstart event"); + equal(this, element[0], "context of event"); + ok(ui.position !== undefined, "ui.position in callback"); + ok(ui.offset !== undefined, "ui.offset in callback"); + }); + + handle = $(".ui-dialog-titlebar", element.dialog("widget")); + TestHelpers.dialog.drag(element, handle, 50, 50); + element.remove(); +}); + +test("drag", function() { + expect(9); + var handle, + hasDragged = false, + element = $("<div></div>").dialog({ + drag: function(ev, ui) { + if (!hasDragged) { + ok(true, "dragging fires drag callback"); + equal(this, element[0], "context of callback"); + equal(ev.type, "dialogdrag", "event type in callback"); + ok(ui.position !== undefined, "ui.position in callback"); + ok(ui.offset !== undefined, "ui.offset in callback"); + + hasDragged = true; + } + } + }).one("dialogdrag", function(ev, ui) { + ok(true, "dragging fires dialogdrag event"); + equal(this, element[0], "context of event"); + ok(ui.position !== undefined, "ui.position in callback"); + ok(ui.offset !== undefined, "ui.offset in callback"); + }); + + handle = $(".ui-dialog-titlebar", element.dialog("widget")); + TestHelpers.dialog.drag(element, handle, 50, 50); + element.remove(); +}); + +test("dragStop", function() { + expect(9); + + var handle, + element = $("<div></div>").dialog({ + dragStop: function(ev, ui) { + ok(true, "dragging fires dragStop callback"); + equal(this, element[0], "context of callback"); + equal(ev.type, "dialogdragstop", "event type in callback"); + ok(ui.position !== undefined, "ui.position in callback"); + ok(ui.offset !== undefined, "ui.offset in callback"); + } + }).bind("dialogdragstop", function(ev, ui) { + ok(true, "dragging fires dialogdragstop event"); + equal(this, element[0], "context of event"); + ok(ui.position !== undefined, "ui.position in callback"); + ok(ui.offset !== undefined, "ui.offset in callback"); + }); + + handle = $(".ui-dialog-titlebar", element.dialog("widget")); + TestHelpers.dialog.drag(element, handle, 50, 50); + element.remove(); +}); + +test("resizeStart", function() { + expect(13); + + var handle, + element = $("<div></div>").dialog({ + resizeStart: function(ev, ui) { + ok(true, "resizing fires resizeStart callback"); + equal(this, element[0], "context of callback"); + equal(ev.type, "dialogresizestart", "event type in callback"); + ok(ui.originalPosition !== undefined, "ui.originalPosition in callback"); + ok(ui.originalSize !== undefined, "ui.originalSize in callback"); + ok(ui.position !== undefined, "ui.position in callback"); + ok(ui.size !== undefined, "ui.size in callback"); + } + }).bind("dialogresizestart", function(ev, ui) { + ok(true, "resizing fires dialogresizestart event"); + equal(this, element[0], "context of event"); + ok(ui.originalPosition !== undefined, "ui.originalPosition in callback"); + ok(ui.originalSize !== undefined, "ui.originalSize in callback"); + ok(ui.position !== undefined, "ui.position in callback"); + ok(ui.size !== undefined, "ui.size in callback"); + }); + + handle = $(".ui-resizable-se", element.dialog("widget")); + TestHelpers.dialog.drag(element, handle, 50, 50); + element.remove(); +}); + +test("resize", function() { + expect(13); + var handle, + hasResized = false, + element = $("<div></div>").dialog({ + resize: function(ev, ui) { + if (!hasResized) { + ok(true, "resizing fires resize callback"); + equal(this, element[0], "context of callback"); + equal(ev.type, "dialogresize", "event type in callback"); + ok(ui.originalPosition !== undefined, "ui.originalPosition in callback"); + ok(ui.originalSize !== undefined, "ui.originalSize in callback"); + ok(ui.position !== undefined, "ui.position in callback"); + ok(ui.size !== undefined, "ui.size in callback"); + + hasResized = true; + } + } + }).one("dialogresize", function(ev, ui) { + ok(true, "resizing fires dialogresize event"); + equal(this, element[0], "context of event"); + ok(ui.originalPosition !== undefined, "ui.originalPosition in callback"); + ok(ui.originalSize !== undefined, "ui.originalSize in callback"); + ok(ui.position !== undefined, "ui.position in callback"); + ok(ui.size !== undefined, "ui.size in callback"); + }); + + handle = $(".ui-resizable-se", element.dialog("widget")); + TestHelpers.dialog.drag(element, handle, 50, 50); + element.remove(); +}); + +test("resizeStop", function() { + expect(13); + + var handle, + element = $("<div></div>").dialog({ + resizeStop: function(ev, ui) { + ok(true, "resizing fires resizeStop callback"); + equal(this, element[0], "context of callback"); + equal(ev.type, "dialogresizestop", "event type in callback"); + ok(ui.originalPosition !== undefined, "ui.originalPosition in callback"); + ok(ui.originalSize !== undefined, "ui.originalSize in callback"); + ok(ui.position !== undefined, "ui.position in callback"); + ok(ui.size !== undefined, "ui.size in callback"); + } + }).bind("dialogresizestop", function(ev, ui) { + ok(true, "resizing fires dialogresizestop event"); + equal(this, element[0], "context of event"); + ok(ui.originalPosition !== undefined, "ui.originalPosition in callback"); + ok(ui.originalSize !== undefined, "ui.originalSize in callback"); + ok(ui.position !== undefined, "ui.position in callback"); + ok(ui.size !== undefined, "ui.size in callback"); + }); + + handle = $(".ui-resizable-se", element.dialog("widget")); + TestHelpers.dialog.drag(element, handle, 50, 50); + element.remove(); +}); + +asyncTest("close", function() { + expect(14); + + var element = $("<div></div>").dialog({ + close: function(ev, ui) { + ok(true, ".dialog('close') fires close callback"); + equal(this, element[0], "context of callback"); + equal(ev.type, "dialogclose", "event type in callback"); + deepEqual(ui, {}, "ui hash in callback"); + } + }).bind("dialogclose", function(ev, ui) { + ok(true, ".dialog('close') fires dialogclose event"); + equal(this, element[0], "context of event"); + deepEqual(ui, {}, "ui hash in event"); + }); + element.dialog("close"); + element.remove(); + + // Close event with an effect + element = $("<div></div>").dialog({ + hide: 10, + close: function(ev, ui) { + ok(true, ".dialog('close') fires close callback"); + equal(this, element[0], "context of callback"); + equal(ev.type, "dialogclose", "event type in callback"); + deepEqual(ui, {}, "ui hash in callback"); + start(); + } + }).bind("dialogclose", function(ev, ui) { + ok(true, ".dialog('close') fires dialogclose event"); + equal(this, element[0], "context of event"); + deepEqual(ui, {}, "ui hash in event"); + }); + element.dialog("close"); +}); + +test("beforeClose", function() { + expect(14); + + var element = $("<div></div>").dialog({ + beforeClose: function(ev, ui) { + ok(true, ".dialog('close') fires beforeClose callback"); + equal(this, element[0], "context of callback"); + equal(ev.type, "dialogbeforeclose", "event type in callback"); + deepEqual(ui, {}, "ui hash in callback"); + return false; + } + }); + + element.dialog("close"); + ok( element.dialog("widget").is(":visible"), "beforeClose callback should prevent dialog from closing"); + element.remove(); + + element = $("<div></div>").dialog(); + element.dialog("option", "beforeClose", function(ev, ui) { + ok(true, ".dialog('close') fires beforeClose callback"); + equal(this, element[0], "context of callback"); + equal(ev.type, "dialogbeforeclose", "event type in callback"); + deepEqual(ui, {}, "ui hash in callback"); + return false; + }); + element.dialog("close"); + + ok( element.dialog("widget").is(":visible"), "beforeClose callback should prevent dialog from closing"); + element.remove(); + + element = $("<div></div>").dialog().bind("dialogbeforeclose", function(ev, ui) { + ok(true, ".dialog('close') triggers dialogbeforeclose event"); + equal(this, element[0], "context of event"); + deepEqual(ui, {}, "ui hash in event"); + return false; + }); + element.dialog("close"); + ok( element.dialog("widget").is(":visible"), "dialogbeforeclose event should prevent dialog from closing"); + element.remove(); +}); + +// #8789 and #8838 +asyncTest("ensure dialog's container doesn't scroll on resize and focus", function() { + expect(2); + + var element = $("#dialog1").dialog(), + initialScroll = $(window).scrollTop(); + element.dialog("option", "height", 600); + equal($(window).scrollTop(), initialScroll, "scroll hasn't moved after height change"); + setTimeout( function(){ + $(".ui-dialog-titlebar-close").simulate("mousedown"); + equal($(window).scrollTop(), initialScroll, "scroll hasn't moved after focus moved to dialog"); + element.dialog("destroy"); + start(); + }, 500); +}); + +test("#5184: isOpen in dialogclose event is true", function() { + expect( 3 ); + + var element = $( "<div></div>" ).dialog({ + close: function() { + ok( !element.dialog("isOpen"), "dialog is not open during close" ); + } + }); + ok( element.dialog("isOpen"), "dialog is open after init" ); + element.dialog( "close" ); + ok( !element.dialog("isOpen"), "dialog is not open after close" ); + element.remove(); +}); + +test("ensure dialog keeps focus when clicking modal overlay", function() { + expect( 2 ); + + var element = $( "<div></div>" ).dialog({ + modal: true + }); + ok( $(":focus").closest(".ui-dialog").length, "focus is in dialog" ); + $(".ui-widget-overlay").simulate("mousedown"); + ok( $(":focus").closest(".ui-dialog").length, "focus is still in dialog" ); + element.remove(); +}); + +})(jQuery); diff --git a/apps/it/static/js/ui/tests/unit/dialog/dialog_methods.js b/apps/it/static/js/ui/tests/unit/dialog/dialog_methods.js new file mode 100644 index 0000000..b9c8cad --- /dev/null +++ b/apps/it/static/js/ui/tests/unit/dialog/dialog_methods.js @@ -0,0 +1,252 @@ +/* + * dialog_methods.js + */ +(function($) { + +module("dialog: methods", { + teardown: function() { + $("body>.ui-dialog").remove(); + } +}); + +test("init", function() { + expect(6); + + $("<div></div>").appendTo("body").dialog().remove(); + ok(true, ".dialog() called on element"); + + $([]).dialog().remove(); + ok(true, ".dialog() called on empty collection"); + + $("<div></div>").dialog().remove(); + ok(true, ".dialog() called on disconnected DOMElement - never connected"); + + $("<div></div>").appendTo("body").remove().dialog().remove(); + ok(true, ".dialog() called on disconnected DOMElement - removed"); + + var element = $("<div></div>").dialog(); + element.dialog("option", "foo"); + element.remove(); + ok(true, "arbitrary option getter after init"); + + $("<div></div>").dialog().dialog("option", "foo", "bar").remove(); + ok(true, "arbitrary option setter after init"); +}); + +test("destroy", function() { + expect( 17 ); + + var element, element2; + + $( "#dialog1, #form-dialog" ).hide(); + domEqual( "#dialog1", function() { + var dialog = $( "#dialog1" ).dialog().dialog( "destroy" ); + equal( dialog.parent()[ 0 ], $( "#qunit-fixture" )[ 0 ] ); + equal( dialog.index(), 0 ); + }); + domEqual( "#form-dialog", function() { + var dialog = $( "#form-dialog" ).dialog().dialog( "destroy" ); + equal( dialog.parent()[ 0 ], $( "#qunit-fixture" )[ 0 ] ); + equal( dialog.index(), 2 ); + }); + + // Ensure dimensions are restored (#8119) + $( "#dialog1" ).show().css({ + width: "400px", + minHeight: "100px", + height: "200px" + }); + domEqual( "#dialog1", function() { + $( "#dialog1" ).dialog().dialog( "destroy" ); + }); + + // Don't throw errors when destroying a never opened modal dialog (#9004) + $( "#dialog1" ).dialog({ autoOpen: false, modal: true }).dialog( "destroy" ); + equal( $( ".ui-widget-overlay" ).length, 0, "overlay does not exist" ); + equal( $.ui.dialog.overlayInstances, 0, "overlayInstances equals the number of open overlays"); + + element = $( "#dialog1" ).dialog({ modal: true }), + element2 = $( "#dialog2" ).dialog({ modal: true }); + equal( $( ".ui-widget-overlay" ).length, 2, "overlays created when dialogs are open" ); + equal( $.ui.dialog.overlayInstances, 2, "overlayInstances equals the number of open overlays" ); + element.dialog( "close" ); + equal( $( ".ui-widget-overlay" ).length, 1, "overlay remains after closing one dialog" ); + equal( $.ui.dialog.overlayInstances, 1, "overlayInstances equals the number of open overlays" ); + element.dialog( "destroy" ); + equal( $( ".ui-widget-overlay" ).length, 1, "overlay remains after destroying one dialog" ); + equal( $.ui.dialog.overlayInstances, 1, "overlayInstances equals the number of open overlays" ); + element2.dialog( "destroy" ); + equal( $( ".ui-widget-overlay" ).length, 0, "overlays removed when all dialogs are destoryed" ); + equal( $.ui.dialog.overlayInstances, 0, "overlayInstances equals the number of open overlays" ); +}); + +asyncTest("#9000: Dialog leaves broken event handler after close/destroy in certain cases", function() { + expect( 1 ); + $( "#dialog1" ).dialog({ modal:true }).dialog( "close" ).dialog( "destroy" ); + setTimeout(function() { + $( "#favorite-animal" ).focus(); + ok( true, "close and destroy modal dialog before its really opened" ); + start(); + }, 2 ); +}); + +test("#4980: Destroy should place element back in original DOM position", function(){ + expect( 2 ); + var container = $("<div id='container'><div id='modal'>Content</div></div>"), + modal = container.find("#modal"); + modal.dialog(); + ok(!$.contains(container[0], modal[0]), "dialog should move modal element to outside container element"); + modal.dialog("destroy"); + ok($.contains(container[0], modal[0]), "dialog(destroy) should place element back in original DOM position"); +}); + +test( "enable/disable disabled", function() { + expect( 2 ); + var element = $( "<div></div>" ).dialog(); + element.dialog( "disable" ); + equal(element.dialog( "option", "disabled" ), false, "disable method doesn't do anything" ); + ok( !element.dialog( "widget" ).hasClass( "ui-dialog-disabled" ), "disable method doesn't add ui-dialog-disabled class" ); +}); + +test("close", function() { + expect( 3 ); + + var element, + expected = $("<div></div>").dialog(), + actual = expected.dialog("close"); + equal(actual, expected, "close is chainable"); + + element = $("<div></div>").dialog(); + ok(element.dialog("widget").is(":visible") && !element.dialog("widget").is(":hidden"), "dialog visible before close method called"); + element.dialog("close"); + ok(element.dialog("widget").is(":hidden") && !element.dialog("widget").is(":visible"), "dialog hidden after close method called"); +}); + +test("isOpen", function() { + expect(4); + + var element = $("<div></div>").dialog(); + equal(element.dialog("isOpen"), true, "dialog is open after init"); + element.dialog("close"); + equal(element.dialog("isOpen"), false, "dialog is closed"); + element.remove(); + + element = $("<div></div>").dialog({autoOpen: false}); + equal(element.dialog("isOpen"), false, "dialog is closed after init"); + element.dialog("open"); + equal(element.dialog("isOpen"), true, "dialog is open"); + element.remove(); +}); + +test("moveToTop", function() { + expect( 5 ); + function order() { + var actual = $( ".ui-dialog" ).map(function() { + return +$( this ).find( ".ui-dialog-content" ).attr( "id" ).replace( /\D+/, "" ); + }).get().reverse(); + deepEqual( actual, $.makeArray( arguments ) ); + } + var dialog1, dialog2, + focusOn = "dialog1"; + dialog1 = $( "#dialog1" ).dialog({ + focus: function() { + equal( focusOn, "dialog1" ); + } + }); + focusOn = "dialog2"; + dialog2 = $( "#dialog2" ).dialog({ + focus: function() { + equal( focusOn, "dialog2" ); + } + }); + order( 2, 1 ); + focusOn = "dialog1"; + dialog1.dialog( "moveToTop" ); + order( 1, 2 ); +}); + +test("open", function() { + expect( 3 ); + var element, + expected = $("<div></div>").dialog(), + actual = expected.dialog("open"); + equal(actual, expected, "open is chainable"); + + element = $("<div></div>").dialog({ autoOpen: false }); + ok(element.dialog("widget").is(":hidden") && !element.dialog("widget").is(":visible"), "dialog hidden before open method called"); + element.dialog("open"); + ok(element.dialog("widget").is(":visible") && !element.dialog("widget").is(":hidden"), "dialog visible after open method called"); +}); + +test("#6137: dialog('open') causes form elements to reset on IE7", function() { + expect(2); + + var d1 = $("<form><input type='radio' name='radio' id='a' value='a' checked='checked'></input>" + + "<input type='radio' name='radio' id='b' value='b'>b</input></form>").appendTo( "body" ).dialog({autoOpen: false}); + + d1.find("#b").prop( "checked", true ); + equal(d1.find("input:checked").val(), "b", "checkbox b is checked"); + + d1.dialog("open"); + equal(d1.find("input:checked").val(), "b", "checkbox b is checked"); + + d1.remove(); +}); + +asyncTest( "#8958: dialog can be opened while opening", function() { + expect( 1 ); + + var element = $( "<div>" ).dialog({ + autoOpen: false, + modal: true, + open: function() { + equal( $( ".ui-widget-overlay" ).length, 1 ); + start(); + } + }); + + // Support: IE8 + // For some reason the #favorite-color input doesn't get focus if we don't + // focus the body first, causing the test to hang. + $( "body" ).focus(); + + $( "#favorite-animal" ) + // We focus the input to start the test. Once it receives focus, the + // dialog will open. Opening the dialog, will cause an element inside + // the dialog to gain focus, thus blurring the input. + .bind( "focus", function() { + element.dialog( "open" ); + }) + // When the input blurs, the dialog is in the process of opening. We + // try to open the dialog again, to make sure that dialogs properly + // handle a call to the open() method during the process of the dialog + // being opened. + .bind( "blur", function() { + element.dialog( "open" ); + }) + .focus(); +}); + +test("#5531: dialog width should be at least minWidth on creation", function () { + expect( 4 ); + var element = $("<div></div>").dialog({ + width: 200, + minWidth: 300 + }); + + equal(element.dialog("option", "width"), 300, "width is minWidth"); + element.dialog("option", "width", 200); + equal(element.dialog("option", "width"), 300, "width unchanged when set to < minWidth"); + element.dialog("option", "width", 320); + equal(element.dialog("option", "width"), 320, "width changed if set to > minWidth"); + element.remove(); + + element = $("<div></div>").dialog({ + minWidth: 300 + }); + ok(element.dialog("option", "width") >= 300, "width is at least 300"); + element.remove(); + +}); + +})(jQuery); diff --git a/apps/it/static/js/ui/tests/unit/dialog/dialog_options.js b/apps/it/static/js/ui/tests/unit/dialog/dialog_options.js new file mode 100644 index 0000000..07c2d68 --- /dev/null +++ b/apps/it/static/js/ui/tests/unit/dialog/dialog_options.js @@ -0,0 +1,584 @@ +/* + * dialog_options.js + */ +(function($) { + +module("dialog: options"); + +test( "appendTo", function() { + expect( 16 ); + var detached = $( "<div>" ), + element = $( "#dialog1" ).dialog({ + modal: true + }); + equal( element.dialog( "widget" ).parent()[0], document.body, "defaults to body" ); + equal( $( ".ui-widget-overlay" ).parent()[0], document.body, "overlay defaults to body" ); + element.dialog( "destroy" ); + + element.dialog({ + appendTo: ".wrap", + modal: true + }); + equal( element.dialog( "widget" ).parent()[0], $( "#wrap1" )[0], "first found element" ); + equal( $( ".ui-widget-overlay" ).parent()[0], $( "#wrap1" )[0], "overlay first found element" ); + equal( $( "#wrap2 .ui-dialog" ).length, 0, "only appends to one element" ); + equal( $( "#wrap2 .ui-widget-overlay" ).length, 0, "overlay only appends to one element" ); + element.dialog( "destroy" ); + + element.dialog({ + appendTo: null, + modal: true + }); + equal( element.dialog( "widget" ).parent()[0], document.body, "null" ); + equal( $( ".ui-widget-overlay" ).parent()[0], document.body, "overlay null" ); + element.dialog( "destroy" ); + + element.dialog({ + autoOpen: false, + modal: true + }).dialog( "option", "appendTo", "#wrap1" ).dialog( "open" ); + equal( element.dialog( "widget" ).parent()[0], $( "#wrap1" )[0], "modified after init" ); + equal( $( ".ui-widget-overlay" ).parent()[0], $( "#wrap1" )[0], "overlay modified after init" ); + element.dialog( "destroy" ); + + element.dialog({ + appendTo: detached, + modal: true + }); + equal( element.dialog( "widget" ).parent()[0], detached[0], "detached jQuery object" ); + equal( detached.find( ".ui-widget-overlay" ).parent()[0], detached[0], "overlay detached jQuery object" ); + element.dialog( "destroy" ); + + element.dialog({ + appendTo: detached[0], + modal: true + }); + equal( element.dialog( "widget" ).parent()[0], detached[0], "detached DOM element" ); + equal( detached.find( ".ui-widget-overlay" ).parent()[0], detached[0], "overlay detached DOM element" ); + element.dialog( "destroy" ); + + element.dialog({ + autoOpen: false, + modal: true + }).dialog( "option", "appendTo", detached ); + equal( element.dialog( "widget" ).parent()[0], detached[0], "detached DOM element via option()" ); + equal( detached.find( ".ui-widget-overlay" ).length, 0, "overlay detached DOM element via option()" ); + element.dialog( "destroy" ); +}); + +test("autoOpen", function() { + expect(2); + + var element = $("<div></div>").dialog({ autoOpen: false }); + ok( !element.dialog("widget").is(":visible"), ".dialog({ autoOpen: false })"); + element.remove(); + + element = $("<div></div>").dialog({ autoOpen: true }); + ok( element.dialog("widget").is(":visible"), ".dialog({ autoOpen: true })"); + element.remove(); +}); + +test("buttons", function() { + expect(21); + + var btn, i, newButtons, + buttons = { + "Ok": function( ev ) { + ok(true, "button click fires callback"); + equal(this, element[0], "context of callback"); + equal(ev.target, btn[0], "event target"); + }, + "Cancel": function( ev ) { + ok(true, "button click fires callback"); + equal(this, element[0], "context of callback"); + equal(ev.target, btn[1], "event target"); + } + }, + element = $("<div></div>").dialog({ buttons: buttons }); + + btn = element.dialog( "widget" ).find( ".ui-dialog-buttonpane button" ); + equal(btn.length, 2, "number of buttons"); + + i = 0; + $.each(buttons, function( key ) { + equal(btn.eq(i).text(), key, "text of button " + (i+1)); + i++; + }); + + ok(btn.parent().hasClass("ui-dialog-buttonset"), "buttons in container"); + ok(element.parent().hasClass("ui-dialog-buttons"), "dialog wrapper adds class about having buttons"); + + btn.trigger("click"); + + newButtons = { + "Close": function( ev ) { + ok(true, "button click fires callback"); + equal(this, element[0], "context of callback"); + equal(ev.target, btn[0], "event target"); + } + }; + + deepEqual(element.dialog("option", "buttons"), buttons, ".dialog('option', 'buttons') getter"); + element.dialog("option", "buttons", newButtons); + deepEqual(element.dialog("option", "buttons"), newButtons, ".dialog('option', 'buttons', ...) setter"); + + btn = element.dialog( "widget" ).find( ".ui-dialog-buttonpane button" ); + equal(btn.length, 1, "number of buttons after setter"); + btn.trigger("click"); + + i = 0; + $.each(newButtons, function( key ) { + equal(btn.eq(i).text(), key, "text of button " + (i+1)); + i += 1; + }); + + element.dialog("option", "buttons", null); + btn = element.dialog( "widget" ).find( ".ui-dialog-buttonpane button" ); + equal(btn.length, 0, "all buttons have been removed"); + equal(element.find(".ui-dialog-buttonset").length, 0, "buttonset has been removed"); + equal(element.parent().hasClass("ui-dialog-buttons"), false, "dialog wrapper removes class about having buttons"); + + element.remove(); +}); + +test("buttons - advanced", function() { + expect( 7 ); + + var buttons, + element = $("<div></div>").dialog({ + buttons: [ + { + text: "a button", + "class": "additional-class", + id: "my-button-id", + click: function() { + equal(this, element[0], "correct context"); + }, + icons: { + primary: "ui-icon-cancel" + }, + showText: false + } + ] + }); + + buttons = element.dialog( "widget" ).find( ".ui-dialog-buttonpane button" ); + equal(buttons.length, 1, "correct number of buttons"); + equal(buttons.attr("id"), "my-button-id", "correct id"); + equal(buttons.text(), "a button", "correct label"); + ok(buttons.hasClass("additional-class"), "additional classes added"); + deepEqual( buttons.button("option", "icons"), { primary: "ui-icon-cancel", secondary: null } ); + equal( buttons.button( "option", "text" ), false ); + buttons.click(); + + element.remove(); +}); + +test("#9043: buttons with Array.prototype modification", function() { + expect( 1 ); + Array.prototype.test = $.noop; + var element = $( "<div></div>" ).dialog(); + equal( element.dialog( "widget" ).find( ".ui-dialog-buttonpane" ).length, 0, + "no button pane" ); + element.remove(); + delete Array.prototype.test; +}); + +test("closeOnEscape", function() { + expect( 6 ); + var element = $("<div></div>").dialog({ closeOnEscape: false }); + ok(true, "closeOnEscape: false"); + ok(element.dialog("widget").is(":visible") && !element.dialog("widget").is(":hidden"), "dialog is open before ESC"); + element.simulate("keydown", { keyCode: $.ui.keyCode.ESCAPE }) + .simulate("keypress", { keyCode: $.ui.keyCode.ESCAPE }) + .simulate("keyup", { keyCode: $.ui.keyCode.ESCAPE }); + ok(element.dialog("widget").is(":visible") && !element.dialog("widget").is(":hidden"), "dialog is open after ESC"); + + element.remove(); + + element = $("<div></div>").dialog({ closeOnEscape: true }); + ok(true, "closeOnEscape: true"); + ok(element.dialog("widget").is(":visible") && !element.dialog("widget").is(":hidden"), "dialog is open before ESC"); + element.simulate("keydown", { keyCode: $.ui.keyCode.ESCAPE }) + .simulate("keypress", { keyCode: $.ui.keyCode.ESCAPE }) + .simulate("keyup", { keyCode: $.ui.keyCode.ESCAPE }); + ok(element.dialog("widget").is(":hidden") && !element.dialog("widget").is(":visible"), "dialog is closed after ESC"); +}); + +test("closeText", function() { + expect(3); + + var element = $("<div></div>").dialog(); + equal(element.dialog("widget").find(".ui-dialog-titlebar-close span").text(), "close", + "default close text"); + element.remove(); + + element = $("<div></div>").dialog({ closeText: "foo" }); + equal(element.dialog("widget").find(".ui-dialog-titlebar-close span").text(), "foo", + "closeText on init"); + element.remove(); + + element = $("<div></div>").dialog().dialog("option", "closeText", "bar"); + equal(element.dialog("widget").find(".ui-dialog-titlebar-close span").text(), "bar", + "closeText via option method"); + element.remove(); +}); + +test("dialogClass", function() { + expect( 6 ); + + var element = $("<div></div>").dialog(); + equal(element.dialog("widget").is(".foo"), false, "dialogClass not specified. foo class added"); + element.remove(); + + element = $("<div></div>").dialog({ dialogClass: "foo" }); + equal(element.dialog("widget").is(".foo"), true, "dialogClass in init. foo class added"); + element.dialog( "option", "dialogClass", "foobar" ); + equal( element.dialog("widget").is(".foo"), false, "dialogClass changed, previous one was removed" ); + equal( element.dialog("widget").is(".foobar"), true, "dialogClass changed, new one was added" ); + element.remove(); + + element = $("<div></div>").dialog({ dialogClass: "foo bar" }); + equal(element.dialog("widget").is(".foo"), true, "dialogClass in init, two classes. foo class added"); + equal(element.dialog("widget").is(".bar"), true, "dialogClass in init, two classes. bar class added"); + element.remove(); +}); + +test("draggable", function() { + expect(4); + + var element = $("<div></div>").dialog({ draggable: false }); + + TestHelpers.dialog.testDrag(element, 50, -50, 0, 0); + element.dialog("option", "draggable", true); + TestHelpers.dialog.testDrag(element, 50, -50, 50, -50); + element.remove(); + + element = $("<div></div>").dialog({ draggable: true }); + TestHelpers.dialog.testDrag(element, 50, -50, 50, -50); + element.dialog("option", "draggable", false); + TestHelpers.dialog.testDrag(element, 50, -50, 0, 0); + element.remove(); +}); + +test("height", function() { + expect(4); + + var element = $("<div></div>").dialog(); + equal(element.dialog("widget").outerHeight(), 150, "default height"); + element.remove(); + + element = $("<div></div>").dialog({ height: 237 }); + equal(element.dialog("widget").outerHeight(), 237, "explicit height"); + element.remove(); + + element = $("<div></div>").dialog(); + element.dialog("option", "height", 238); + equal(element.dialog("widget").outerHeight(), 238, "explicit height set after init"); + element.remove(); + + element = $("<div></div>").css("padding", "20px") + .dialog({ height: 240 }); + equal(element.dialog("widget").outerHeight(), 240, "explicit height with padding"); + element.remove(); +}); + +asyncTest( "hide, #5860 - don't leave effects wrapper behind", function() { + expect( 1 ); + $( "#dialog1" ).dialog({ hide: "clip" }).dialog( "close" ).dialog( "destroy" ); + setTimeout(function() { + equal( $( ".ui-effects-wrapper" ).length, 0 ); + start(); + }, 500); +}); + +test("maxHeight", function() { + expect(3); + + var element = $("<div></div>").dialog({ maxHeight: 200 }); + TestHelpers.dialog.drag(element, ".ui-resizable-s", 1000, 1000); + closeEnough(element.dialog("widget").height(), 200, 1, "maxHeight"); + element.remove(); + + element = $("<div></div>").dialog({ maxHeight: 200 }); + TestHelpers.dialog.drag(element, ".ui-resizable-n", -1000, -1000); + closeEnough(element.dialog("widget").height(), 200, 1, "maxHeight"); + element.remove(); + + element = $("<div></div>").dialog({ maxHeight: 200 }).dialog("option", "maxHeight", 300); + TestHelpers.dialog.drag(element, ".ui-resizable-s", 1000, 1000); + closeEnough(element.dialog("widget").height(), 300, 1, "maxHeight"); + element.remove(); +}); + +test("maxWidth", function() { + expect(3); + + var element = $("<div></div>").dialog({ maxWidth: 200 }); + TestHelpers.dialog.drag(element, ".ui-resizable-e", 1000, 1000); + closeEnough(element.dialog("widget").width(), 200, 1, "maxWidth"); + element.remove(); + + element = $("<div></div>").dialog({ maxWidth: 200 }); + TestHelpers.dialog.drag(element, ".ui-resizable-w", -1000, -1000); + closeEnough(element.dialog("widget").width(), 200, 1, "maxWidth"); + element.remove(); + + element = $("<div></div>").dialog({ maxWidth: 200 }).dialog("option", "maxWidth", 300); + TestHelpers.dialog.drag(element, ".ui-resizable-w", -1000, -1000); + closeEnough(element.dialog("widget").width(), 300, 1, "maxWidth"); + element.remove(); +}); + +test("minHeight", function() { + expect(3); + + var element = $("<div></div>").dialog({ minHeight: 10 }); + TestHelpers.dialog.drag(element, ".ui-resizable-s", -1000, -1000); + closeEnough(element.dialog("widget").height(), 10, 1, "minHeight"); + element.remove(); + + element = $("<div></div>").dialog({ minHeight: 10 }); + TestHelpers.dialog.drag(element, ".ui-resizable-n", 1000, 1000); + closeEnough(element.dialog("widget").height(), 10, 1, "minHeight"); + element.remove(); + + element = $("<div></div>").dialog({ minHeight: 10 }).dialog("option", "minHeight", 30); + TestHelpers.dialog.drag(element, ".ui-resizable-n", 1000, 1000); + closeEnough(element.dialog("widget").height(), 30, 1, "minHeight"); + element.remove(); +}); + +test("minWidth", function() { + expect(3); + + var element = $("<div></div>").dialog({ minWidth: 10 }); + TestHelpers.dialog.drag(element, ".ui-resizable-e", -1000, -1000); + closeEnough(element.dialog("widget").width(), 10, 1, "minWidth"); + element.remove(); + + element = $("<div></div>").dialog({ minWidth: 10 }); + TestHelpers.dialog.drag(element, ".ui-resizable-w", 1000, 1000); + closeEnough(element.dialog("widget").width(), 10, 1, "minWidth"); + element.remove(); + + element = $("<div></div>").dialog({ minWidth: 30 }).dialog("option", "minWidth", 30); + TestHelpers.dialog.drag(element, ".ui-resizable-w", 1000, 1000); + closeEnough(element.dialog("widget").width(), 30, 1, "minWidth"); + element.remove(); +}); + +test( "position, default center on window", function() { + expect( 2 ); + + // dialogs alter the window width and height in FF and IE7 + // so we collect that information before creating the dialog + // Support: FF, IE7 + var winWidth = $( window ).width(), + winHeight = $( window ).height(), + element = $("<div></div>").dialog(), + dialog = element.dialog("widget"), + offset = dialog.offset(); + closeEnough( offset.left, Math.round( winWidth / 2 - dialog.outerWidth() / 2 ) + $( window ).scrollLeft(), 1, "dialog left position of center on window on initilization" ); + closeEnough( offset.top, Math.round( winHeight / 2 - dialog.outerHeight() / 2 ) + $( window ).scrollTop(), 1, "dialog top position of center on window on initilization" ); + element.remove(); +}); + +test( "position, right bottom at right bottom via ui.position args", function() { + expect( 2 ); + + // dialogs alter the window width and height in FF and IE7 + // so we collect that information before creating the dialog + // Support: FF, IE7 + var winWidth = $( window ).width(), + winHeight = $( window ).height(), + element = $("<div></div>").dialog({ + position: { + my: "right bottom", + at: "right bottom" + } + }), + dialog = element.dialog("widget"), + offset = dialog.offset(); + + closeEnough( offset.left, winWidth - dialog.outerWidth() + $( window ).scrollLeft(), 1, "dialog left position of right bottom at right bottom on initilization" ); + closeEnough( offset.top, winHeight - dialog.outerHeight() + $( window ).scrollTop(), 1, "dialog top position of right bottom at right bottom on initilization" ); + element.remove(); +}); + +test( "position, at another element", function() { + expect( 4 ); + var parent = $("<div></div>").css({ + position: "absolute", + top: 400, + left: 600, + height: 10, + width: 10 + }).appendTo("body"), + + element = $("<div></div>").dialog({ + position: { + my: "left top", + at: "left top", + of: parent, + collision: "none" + } + }), + + dialog = element.dialog("widget"), + offset = dialog.offset(); + + closeEnough( offset.left, 600, 1, "dialog left position at another element on initilization" ); + closeEnough( offset.top, 400, 1, "dialog top position at another element on initilization" ); + + element.dialog("option", "position", { + my: "left top", + at: "right bottom", + of: parent, + collision: "none" + }); + + offset = dialog.offset(); + + closeEnough( offset.left, 610, 1, "dialog left position at another element via setting option" ); + closeEnough( offset.top, 410, 1, "dialog top position at another element via setting option" ); + + element.remove(); + parent.remove(); +}); + +test("resizable", function() { + expect(4); + + var element = $("<div></div>").dialog(); + TestHelpers.dialog.shouldResize(element, 50, 50, "[default]"); + element.dialog("option", "resizable", false); + TestHelpers.dialog.shouldResize(element, 0, 0, "disabled after init"); + element.remove(); + + element = $("<div></div>").dialog({ resizable: false }); + TestHelpers.dialog.shouldResize(element, 0, 0, "disabled in init options"); + element.dialog("option", "resizable", true); + TestHelpers.dialog.shouldResize(element, 50, 50, "enabled after init"); + element.remove(); +}); + +test( "title", function() { + expect( 11 ); + + function titleText() { + return element.dialog("widget").find( ".ui-dialog-title" ).html(); + } + + var element = $( "<div></div>" ).dialog(); + // some browsers return a non-breaking space and some return " " + // so we generate a non-breaking space for comparison + equal( titleText(), $( "<span> </span>" ).html(), "[default]" ); + equal( element.dialog( "option", "title" ), null, "option not changed" ); + element.remove(); + + element = $( "<div title='foo'>" ).dialog(); + equal( titleText(), "foo", "title in element attribute" ); + equal( element.dialog( "option", "title"), "foo", "option updated from attribute" ); + element.remove(); + + element = $( "<div></div>" ).dialog({ title: "foo" }); + equal( titleText(), "foo", "title in init options" ); + equal( element.dialog("option", "title"), "foo", "opiton set from options hash" ); + element.remove(); + + element = $( "<div title='foo'>" ).dialog({ title: "bar" }); + equal( titleText(), "bar", "title in init options should override title in element attribute" ); + equal( element.dialog("option", "title"), "bar", "opiton set from options hash" ); + element.remove(); + + element = $( "<div></div>" ).dialog().dialog( "option", "title", "foo" ); + equal( titleText(), "foo", "title after init" ); + element.remove(); + + // make sure attroperties are properly ignored - #5742 - .attr() might return a DOMElement + element = $( "<form><input name='title'></form>" ).dialog(); + // some browsers return a non-breaking space and some return " " + // so we get the text to normalize to the actual non-breaking space + equal( titleText(), $( "<span> </span>" ).html(), "[default]" ); + equal( element.dialog( "option", "title" ), null, "option not changed" ); + element.remove(); +}); + +test("width", function() { + expect(3); + + var element = $("<div></div>").dialog(); + closeEnough(element.dialog("widget").width(), 300, 1, "default width"); + element.remove(); + + element = $("<div></div>").dialog({width: 437 }); + closeEnough(element.dialog("widget").width(), 437, 1, "explicit width"); + element.dialog("option", "width", 438); + closeEnough(element.dialog("widget").width(), 438, 1, "explicit width after init"); + element.remove(); +}); + +test("#4826: setting resizable false toggles resizable on dialog", function() { + expect(6); + var i, + element = $("<div></div>").dialog({ resizable: false }); + + TestHelpers.dialog.shouldResize(element, 0, 0, "[default]"); + for (i=0; i<2; i++) { + element.dialog("close").dialog("open"); + TestHelpers.dialog.shouldResize(element, 0, 0, "initialized with resizable false toggle ("+ (i+1) +")"); + } + element.remove(); + + element = $("<div></div>").dialog({ resizable: true }); + TestHelpers.dialog.shouldResize(element, 50, 50, "[default]"); + for (i=0; i<2; i++) { + element.dialog("close").dialog("option", "resizable", false).dialog("open"); + TestHelpers.dialog.shouldResize(element, 0, 0, "set option resizable false toggle ("+ (i+1) +")"); + } + element.remove(); + +}); + +asyncTest( "#8051 - 'Explode' dialog animation causes crash in IE 6, 7 and 8", function() { + expect( 1 ); + var element = $( "<div></div>" ).dialog({ + show: "explode", + focus: function() { + ok( true, "dialog opened with animation" ); + element.remove(); + start(); + } + }); +}); + +asyncTest( "#4421 - Focus lost from dialog which uses show-effect", function() { + expect( 1 ); + var element = $( "<div></div>" ).dialog({ + show: "blind", + focus: function() { + equal( element.dialog( "widget" ).find( ":focus" ).length, 1, "dialog maintains focus" ); + element.remove(); + start(); + } + }); +}); + +asyncTest( "Open followed by close during show effect", function() { + expect( 1 ); + var element = $( "<div></div>" ).dialog({ + show: "blind", + close: function() { + ok( true, "dialog closed properly during animation" ); + element.remove(); + start(); + } + }); + + setTimeout( function() { + element.dialog("close"); + }, 100 ); +}); + +})(jQuery); diff --git a/apps/it/static/js/ui/tests/unit/dialog/dialog_test_helpers.js b/apps/it/static/js/ui/tests/unit/dialog/dialog_test_helpers.js new file mode 100644 index 0000000..64e8dbf --- /dev/null +++ b/apps/it/static/js/ui/tests/unit/dialog/dialog_test_helpers.js @@ -0,0 +1,44 @@ +TestHelpers.dialog = { + drag: function(element, handle, dx, dy) { + var d = element.dialog("widget"); + //this mouseover is to work around a limitation in resizable + //TODO: fix resizable so handle doesn't require mouseover in order to be used + $( handle, d ).simulate("mouseover").simulate( "drag", { + dx: dx, + dy: dy + }); + }, + testDrag: function(element, dx, dy, expectedDX, expectedDY, msg) { + var actualDX, actualDY, offsetAfter, + d = element.dialog("widget"), + handle = $(".ui-dialog-titlebar", d), + offsetBefore = d.offset(); + + TestHelpers.dialog.drag(element, handle, dx, dy); + + offsetAfter = d.offset(); + + msg = msg ? msg + "." : ""; + + actualDX = offsetAfter.left - offsetBefore.left; + actualDY = offsetAfter.top - offsetBefore.top; + ok( expectedDX - actualDX <= 1 && expectedDY - actualDY <= 1, "dragged[" + expectedDX + ", " + expectedDY + "] " + msg); + }, + shouldResize: function(element, dw, dh, msg) { + var heightAfter, widthAfter, actual, expected, + d = element.dialog("widget"), + handle = $(".ui-resizable-se", d), + heightBefore = d.height(), + widthBefore = d.width(); + + TestHelpers.dialog.drag(element, handle, 50, 50); + + heightAfter = d.height(); + widthAfter = d.width(); + + msg = msg ? msg + "." : ""; + actual = { width: widthAfter, height: heightAfter }, + expected = { width: widthBefore + dw, height: heightBefore + dh }; + deepEqual(actual, expected, "resized[" + 50 + ", " + 50 + "] " + msg); + } +}; diff --git a/apps/it/static/js/ui/tests/unit/draggable/all.html b/apps/it/static/js/ui/tests/unit/draggable/all.html new file mode 100644 index 0000000..7c43866 --- /dev/null +++ b/apps/it/static/js/ui/tests/unit/draggable/all.html @@ -0,0 +1,30 @@ +<!doctype html> +<html lang="en"> +<head> + <meta charset="utf-8"> + <title>jQuery UI Draggable Test Suite</title> + + <script src="../../../jquery-1.10.2.js"></script> + + <link rel="stylesheet" href="../../../external/qunit.css"> + <link rel="stylesheet" href="../qunit-composite.css"> + <script src="../../../external/qunit.js"></script> + <script src="../qunit-composite.js"></script> + <script src="../subsuite.js"></script> + + <script> + testAllVersions( "draggable" ); + </script> +</head> +<body> + +<h1 id="qunit-header">jQuery UI Draggable Test Suite</h1> +<h2 id="qunit-banner"></h2> +<div id="qunit-testrunner-toolbar"></div> +<h2 id="qunit-userAgent"></h2> +<ol id="qunit-tests"></ol> +<div id="qunit-fixture"> + +</div> +</body> +</html> diff --git a/apps/it/static/js/ui/tests/unit/draggable/draggable.html b/apps/it/static/js/ui/tests/unit/draggable/draggable.html new file mode 100644 index 0000000..3d1d332 --- /dev/null +++ b/apps/it/static/js/ui/tests/unit/draggable/draggable.html @@ -0,0 +1,58 @@ +<!doctype html> +<html lang="en"> +<head> + <meta charset="utf-8"> + <title>jQuery UI Draggable Test Suite</title> + + <script src="../../jquery.js"></script> + <link rel="stylesheet" href="../../../external/qunit.css"> + + <style> + /* See #9077 */ + #draggable3, #draggable4 { z-index: 100; } + </style> + + <script src="../../../external/qunit.js"></script> + <script src="../../jquery.simulate.js"></script> + <script src="../testsuite.js"></script> + <script> + TestHelpers.loadResources({ + css: [ "ui.core" ], + js: [ + "ui/jquery.ui.core.js", + "ui/jquery.ui.widget.js", + "ui/jquery.ui.mouse.js", + "ui/jquery.ui.resizable.js", + "ui/jquery.ui.draggable.js", + "ui/jquery.ui.droppable.js" + ] + }); + </script> + + <script src="draggable_common.js"></script> + <script src="draggable_core.js"></script> + <script src="draggable_events.js"></script> + <script src="draggable_methods.js"></script> + <script src="draggable_options.js"></script> + <script src="draggable_test_helpers.js"></script> + + <script src="../swarminject.js"></script> +</head> +<body> + +<h1 id="qunit-header">jQuery UI Draggable Test Suite</h1> +<h2 id="qunit-banner"></h2> +<div id="qunit-testrunner-toolbar"></div> +<h2 id="qunit-userAgent"></h2> +<ol id="qunit-tests"></ol> +<div id="qunit-fixture"> + <div id="main"></div> + <div id="draggable1" style="background: green; width: 200px; height: 100px;">Relative</div> + <div id="draggable2" style="background: green; width: 200px; height: 100px; position: absolute; top: 10px; left: 10px;"><span><em>Absolute</em></span></div> + <div id="droppable" style="background: green; width: 200px; height: 100px; position: absolute; top: 110px; left: 110px;"><span>Absolute</span></div> + <div style="width: 1px; height: 1000px;"></div> + <div style="position: absolute; width: 1px; height: 2000px;"></div> +</div> + +</body> +</html> diff --git a/apps/it/static/js/ui/tests/unit/draggable/draggable_common.js b/apps/it/static/js/ui/tests/unit/draggable/draggable_common.js new file mode 100644 index 0000000..5abd09e --- /dev/null +++ b/apps/it/static/js/ui/tests/unit/draggable/draggable_common.js @@ -0,0 +1,40 @@ +TestHelpers.commonWidgetTests( "draggable", { + defaults: { + appendTo: "parent", + axis: false, + cancel: "input,textarea,button,select,option", + connectToSortable: false, + containment: false, + cursor: "auto", + cursorAt: false, + disabled: false, + grid: false, + handle: false, + helper: "original", + opacity: false, + refreshPositions: false, + revert: false, + revertDuration: 500, + scroll: true, + scrollSensitivity: 20, + scrollSpeed: 20, + scope: "default", + snap: false, + snapMode: "both", + snapTolerance: 20, + stack: false, + zIndex: false, + + //todo: remove the following option checks when interactions are rewritten: + addClasses: true, + delay: 0, + distance: 1, + iframeFix: false, + + // callbacks + create: null, + drag: null, + start: null, + stop: null + } +}); diff --git a/apps/it/static/js/ui/tests/unit/draggable/draggable_core.js b/apps/it/static/js/ui/tests/unit/draggable/draggable_core.js new file mode 100644 index 0000000..7b1588a --- /dev/null +++ b/apps/it/static/js/ui/tests/unit/draggable/draggable_core.js @@ -0,0 +1,168 @@ +/* + * draggable_core.js + */ + +(function( $ ) { + +module( "draggable: core" ); + +test( "element types", function() { + var typeNames = ( + "p,h1,h2,h3,h4,h5,h6,blockquote,ol,ul,dl,div,form" + + ",table,fieldset,address,ins,del,em,strong,q,cite,dfn,abbr" + + ",acronym,code,samp,kbd,var,img,hr" + + ",input,button,label,select,iframe" + ).split(","); + + expect( typeNames.length * 2 ); + + $.each( typeNames, function( i ) { + var offsetBefore, offsetAfter, + typeName = typeNames[ i ], + el = $( document.createElement( typeName ) ).appendTo("#qunit-fixture"); + + if ( typeName === "table" ) { + el.append("<tr><td>content</td></tr>"); + } + + el.draggable({ cancel: "" }); + offsetBefore = el.offset(); + el.simulate( "drag", { + dx: 50, + dy: 50 + }); + offsetAfter = el.offset(); + + // Support: FF, Chrome, and IE9, + // there are some rounding errors in so we can't say equal, we have to settle for close enough + closeEnough( offsetBefore.left, offsetAfter.left - 50, 1, "dragged[50, 50] " + "<" + typeName + ">" ); + closeEnough( offsetBefore.top, offsetAfter.top - 50, 1, "dragged[50, 50] " + "<" + typeName + ">" ); + el.draggable("destroy"); + el.remove(); + }); +}); + +test( "No options, relative", function() { + expect( 1 ); + TestHelpers.draggable.shouldMove( $( "#draggable1" ).draggable() ); +}); + +test( "No options, absolute", function() { + expect( 1 ); + TestHelpers.draggable.shouldMove( $( "#draggable2" ).draggable() ); +}); + +test( "resizable handle with complex markup (#8756 / #8757)", function() { + expect( 2 ); + + $( "#draggable1" ) + .append( + $("<div>") + .addClass("ui-resizable-handle ui-resizable-w") + .append( $("<div>") ) + ); + + var handle = $(".ui-resizable-w div"), + target = $( "#draggable1" ).draggable().resizable({ handles: "all" }); + + // todo: fix resizable so it doesn't require a mouseover + handle.simulate("mouseover").simulate( "drag", { dx: -50 } ); + equal( target.width(), 250, "compare width" ); + + // todo: fix resizable so it doesn't require a mouseover + handle.simulate("mouseover").simulate( "drag", { dx: 50 } ); + equal( target.width(), 200, "compare width" ); +}); + +test( "#8269: Removing draggable element on drop", function() { + expect( 1 ); + + var element = $( "#draggable1" ).wrap( "<div id='wrapper' />" ).draggable(), + dropOffset = $( "#droppable" ).offset(); + + $( "#droppable" ).droppable({ + drop: function() { + $( "#wrapper" ).remove(); + ok( true, "element removed from DOM on drop" ); + } + }); + + // Support: Opera 12.10, Safari 5.1, jQuery <1.8 + if ( TestHelpers.draggable.unreliableContains ) { + ok( true, "Opera <12.14 and Safari <6.0 report wrong values for $.contains in jQuery < 1.8" ); + ok( true, "Opera <12.14 and Safari <6.0 report wrong values for $.contains in jQuery < 1.8" ); + } else { + element.simulate( "drag", { + handle: "corner", + x: dropOffset.left, + y: dropOffset.top + }); + } +}); + +test( "#6258: not following mouse when scrolled and using overflow-y: scroll", function() { + expect( 2 ); + + var element = $( "#draggable1" ).draggable({ + stop: function( event, ui ) { + equal( ui.position.left, 1, "left position is correct despite overflow on HTML" ); + equal( ui.position.top, 1, "top position is correct despite overflow on HTML" ); + $( "html" ) + .css( "overflow-y", oldOverflowY ) + .css( "overflow-x", oldOverflowX ) + .scrollTop( 0 ) + .scrollLeft( 0 ); + } + }), + contentToForceScroll = $( "<div>" ).css({ + height: "10000px", + width: "10000px" + }), + oldOverflowY = $( "html" ).css( "overflow-y" ), + oldOverflowX = $( "html" ).css( "overflow-x" ); + + contentToForceScroll.appendTo( "#qunit-fixture" ); + $( "html" ) + .css( "overflow-y", "scroll" ) + .css( "overflow-x", "scroll" ) + .scrollTop( 300 ) + .scrollLeft( 300 ); + + element.simulate( "drag", { + dx: 1, + dy: 1, + moves: 1 + }); +}); + +test( "#5009: scroll not working with parent's position fixed", function() { + expect( 2 ); + + var startValue = 300, + element = $( "#draggable1" ).wrap( "<div id='wrapper' />" ).draggable({ + drag: function() { + startValue += 100; + $( document ).scrollTop( startValue ).scrollLeft( startValue ); + }, + stop: function( event, ui ) { + equal( ui.position.left, 10, "left position is correct when parent position is fixed" ); + equal( ui.position.top, 10, "top position is correct when parent position is fixed" ); + $( document ).scrollTop( 0 ).scrollLeft( 0 ); + } + }), + contentToForceScroll = $( "<div>" ).css({ + height: "20000px", + width: "20000px" + }); + + $( "#qunit-fixture" ).append( contentToForceScroll ); + $( "#wrapper" ).css( "position", "fixed" ); + + element.simulate( "drag", { + dx: 10, + dy: 10, + moves: 3 + }); +}); + +})( jQuery ); diff --git a/apps/it/static/js/ui/tests/unit/draggable/draggable_events.js b/apps/it/static/js/ui/tests/unit/draggable/draggable_events.js new file mode 100644 index 0000000..199561b --- /dev/null +++ b/apps/it/static/js/ui/tests/unit/draggable/draggable_events.js @@ -0,0 +1,125 @@ +/* + * draggable_events.js + */ +(function( $ ) { + +var element; + +module( "draggable: events", { + setup: function() { + element = $("<div>").appendTo("#qunit-fixture"); + }, + teardown: function() { + element.draggable("destroy"); + } +}); + +test( "callbacks occurrence count", function() { + expect( 3 ); + + var start = 0, + stop = 0, + dragc = 0; + + element.draggable({ + start: function() { + start++; + }, + drag: function() { + dragc++; + }, + stop: function() { + stop++; + } + }); + + element.simulate( "drag", { + dx: 10, + dy: 10 + }); + + equal( start, 1, "start callback should happen exactly once" ); + equal( dragc, 3, "drag callback should happen exactly once per mousemove" ); + equal( stop, 1, "stop callback should happen exactly once" ); +}); + +test( "stopping the start callback", function() { + expect( 3 ); + + var start = 0, + stop = 0, + dragc = 0; + + element.draggable({ + start: function() { + start++; + return false; + }, + drag: function() { + dragc++; + }, + stop: function() { + stop++; + } + }); + + element.simulate( "drag", { + dx: 10, + dy: 10 + }); + + equal( start, 1, "start callback should happen exactly once" ); + equal( dragc, 0, "drag callback should not happen at all" ); + equal( stop, 0, "stop callback should not happen if there wasnt even a start" ); +}); + +test( "stopping the drag callback", function() { + expect( 2 ); + + var start = 0, + stop = 0, + dragc = 0; + + element.draggable({ + start: function() { + start++; + }, + drag: function() { + dragc++; + return false; + }, + stop: function() { + stop++; + } + }); + + element.simulate( "drag", { + dx: 10, + dy: 10 + }); + + equal( start, 1, "start callback should happen exactly once" ); + equal( stop, 1, "stop callback should happen, as we need to actively stop the drag" ); +}); + +test( "stopping the stop callback", function() { + expect( 1 ); + + element.draggable({ + helper: "clone", + stop: function() { + return false; + } + }); + + element.simulate( "drag", { + dx: 10, + dy: 10 + }); + + ok( element.data("ui-draggable").helper, "the clone should not be deleted if the stop callback is stopped" ); + + +}); + +})( jQuery ); diff --git a/apps/it/static/js/ui/tests/unit/draggable/draggable_methods.js b/apps/it/static/js/ui/tests/unit/draggable/draggable_methods.js new file mode 100644 index 0000000..901c261 --- /dev/null +++ b/apps/it/static/js/ui/tests/unit/draggable/draggable_methods.js @@ -0,0 +1,99 @@ +/* + * draggable_methods.js + */ +(function( $ ) { + +var element; + +module( "draggable: methods", { + setup: function() { + element = $("<div style='background: green; width: 200px; height: 100px; position: absolute; top: 10px; left: 10px;'><span>Absolute</span></div>").appendTo("#qunit-fixture"); + }, + teardown: function() { + element.remove(); + } +}); + +test( "init", function() { + expect( 5 ); + + element.draggable(); + ok( true, ".draggable() called on element" ); + + $([]).draggable(); + ok( true, ".draggable() called on empty collection" ); + + $("<div></div>").draggable(); + ok( true, ".draggable() called on disconnected DOMElement" ); + + element.draggable( "option", "foo" ); + ok( true, "arbitrary option getter after init" ); + + element.draggable( "option", "foo", "bar" ); + ok( true, "arbitrary option setter after init" ); +}); + +test( "destroy", function() { + expect( 4 ); + + element.draggable().draggable("destroy"); + ok( true, ".draggable('destroy') called on element" ); + + $([]).draggable().draggable("destroy"); + ok( true, ".draggable('destroy') called on empty collection" ); + + element.draggable().draggable("destroy"); + ok( true, ".draggable('destroy') called on disconnected DOMElement" ); + + var expected = element.draggable(), + actual = expected.draggable("destroy"); + equal( actual, expected, "destroy is chainable" ); +}); + +test( "enable", function() { + expect( 7 ); + + element.draggable({ disabled: true }); + TestHelpers.draggable.shouldNotMove( element, ".draggable({ disabled: true })" ); + + element.draggable("enable"); + TestHelpers.draggable.shouldMove( element, ".draggable('enable')" ); + equal( element.draggable( "option", "disabled" ), false, "disabled option getter" ); + + element.draggable("destroy"); + element.draggable({ disabled: true }); + TestHelpers.draggable.shouldNotMove( element, ".draggable({ disabled: true })" ); + + element.draggable( "option", "disabled", false ); + equal(element.draggable( "option", "disabled" ), false, "disabled option setter" ); + TestHelpers.draggable.shouldMove( element, ".draggable('option', 'disabled', false)" ); + + var expected = element.draggable(), + actual = expected.draggable("enable"); + equal( actual, expected, "enable is chainable" ); +}); + +test( "disable", function() { + expect( 7 ); + + element = $("#draggable2").draggable({ disabled: false }); + TestHelpers.draggable.shouldMove( element, ".draggable({ disabled: false })" ); + + element.draggable("disable"); + TestHelpers.draggable.shouldNotMove( element, ".draggable('disable')" ); + equal( element.draggable( "option", "disabled" ), true, "disabled option getter" ); + + element.draggable("destroy"); + element.draggable({ disabled: false }); + TestHelpers.draggable.shouldMove( element, ".draggable({ disabled: false })" ); + + element.draggable( "option", "disabled", true ); + equal( element.draggable( "option", "disabled" ), true, "disabled option setter" ); + TestHelpers.draggable.shouldNotMove( element, ".draggable('option', 'disabled', true)" ); + + var expected = element.draggable(), + actual = expected.draggable("disable"); + equal( actual, expected, "disable is chainable" ); +}); + +})( jQuery ); diff --git a/apps/it/static/js/ui/tests/unit/draggable/draggable_options.js b/apps/it/static/js/ui/tests/unit/draggable/draggable_options.js new file mode 100644 index 0000000..635d318 --- /dev/null +++ b/apps/it/static/js/ui/tests/unit/draggable/draggable_options.js @@ -0,0 +1,1404 @@ +(function( $ ) { + +module( "draggable: options" ); + +// TODO: This doesn't actually test whether append happened, possibly remove +test( "{ appendTo: 'parent' }, default, no clone", function() { + expect( 2 ); + var element = $( "#draggable2" ).draggable({ appendTo: "parent" }); + TestHelpers.draggable.shouldMove( element ); + + element = $( "#draggable1" ).draggable({ appendTo: "parent" }); + TestHelpers.draggable.shouldMove( element ); +}); + +// TODO: This doesn't actually test whether append happened, possibly remove +test( "{ appendTo: Element }, no clone", function() { + expect( 2 ); + var element = $( "#draggable2" ).draggable({ appendTo: $( "#draggable2" ).parent()[ 0 ] }); + + TestHelpers.draggable.shouldMove( element ); + + element = $( "#draggable1" ).draggable({ appendTo: $( "#draggable2" ).parent()[ 0 ] }); + TestHelpers.draggable.shouldMove( element ); +}); + +// TODO: This doesn't actually test whether append happened, possibly remove +test( "{ appendTo: Selector }, no clone", function() { + expect( 2 ); + var element = $( "#draggable2" ).draggable({ appendTo: "#main" }); + TestHelpers.draggable.shouldMove( element ); + + element = $( "#draggable1" ).draggable({ appendTo: "#main" }); + TestHelpers.draggable.shouldMove( element ); +}); + +test( "{ appendTo: 'parent' }, default", function() { + expect( 2 ); + + var element = $( "#draggable1" ).draggable(); + + TestHelpers.draggable.trackAppendedParent( element ); + + equal( element.draggable( "option", "appendTo" ), "parent" ); + + TestHelpers.draggable.move( element, 1, 1 ); + equal( element.data( "last_dragged_parent" ), $( "#qunit-fixture" )[ 0 ] ); +}); + +test( "{ appendTo: Element }", function() { + expect( 1 ); + + var appendTo = $( "#draggable2" ).parent()[ 0 ], + element = $( "#draggable1" ).draggable({ appendTo: appendTo }); + + TestHelpers.draggable.trackAppendedParent( element ); + + TestHelpers.draggable.move( element, 1, 1 ); + equal( element.data( "last_dragged_parent" ), appendTo ); +}); + +test( "{ appendTo: jQuery }", function() { + expect( 1 ); + + var appendTo = $( "#draggable2" ).parent(), + element = $( "#draggable1" ).draggable({ appendTo: appendTo }); + + TestHelpers.draggable.trackAppendedParent( element ); + + TestHelpers.draggable.move( element, 1, 1 ); + equal( element.data( "last_dragged_parent" ), appendTo[ 0 ] ); +}); + +test( "{ appendTo: Selector }", function() { + expect( 1 ); + + var appendTo = "#main", + element = $( "#draggable1" ).draggable({ appendTo: appendTo }); + + TestHelpers.draggable.trackAppendedParent( element ); + + TestHelpers.draggable.move( element, 1, 1 ); + equal( element.data( "last_dragged_parent" ), $(appendTo)[ 0 ] ); +}); + +test( "appendTo, default, switching after initialization", function() { + expect( 2 ); + + var element = $( "#draggable1" ).draggable({ helper : "clone" }); + + TestHelpers.draggable.trackAppendedParent( element ); + + // Move and make sure element was appended to fixture + TestHelpers.draggable.move( element, 1, 1 ); + equal( element.data( "last_dragged_parent" ), $( "#qunit-fixture" )[ 0 ] ); + + // Move and make sure element was appended to main + element.draggable( "option", "appendTo", $( "#main" ) ); + TestHelpers.draggable.move( element, 2, 2 ); + equal( element.data( "last_dragged_parent" ), $( "#main" )[ 0 ] ); +}); + +test( "{ axis: false }, default", function() { + expect( 1 ); + var element = $( "#draggable2" ).draggable({ axis: false }); + TestHelpers.draggable.shouldMove( element ); +}); + +test( "{ axis: 'x' }", function() { + expect( 1 ); + var element = $( "#draggable2" ).draggable({ axis: "x" }); + TestHelpers.draggable.testDrag( element, element, 50, 50, 50, 0 ); +}); + +test( "{ axis: 'y' }", function() { + expect( 1 ); + var element = $( "#draggable2" ).draggable({ axis: "y" }); + TestHelpers.draggable.testDrag( element, element, 50, 50, 0, 50 ); +}); + +test( "{ axis: ? }, unexpected", function() { + var element, + unexpected = { + "true": true, + "{}": {}, + "[]": [], + "null": null, + "undefined": undefined, + "function() {}": function() {} + }; + + expect( 6 ); + + $.each(unexpected, function(key, val) { + element = $( "#draggable2" ).draggable({ axis: val }); + TestHelpers.draggable.testDrag( element, element, 50, 50, 50, 50, "axis: " + key ); + element.draggable( "destroy" ); + }); +}); + +test( "axis, default, switching after initialization", function() { + expect( 3 ); + + var element = $( "#draggable1" ).draggable({ axis : false }); + + // Any Direction + TestHelpers.draggable.testDrag( element, element, 50, 50, 50, 50 ); + + // Only horizontal + element.draggable( "option", "axis", "x" ); + TestHelpers.draggable.testDrag( element, element, 50, 50, 50, 0 ); + + // Vertical only + element.draggable( "option", "axis", "y" ); + TestHelpers.draggable.testDrag( element, element, 50, 50, 0, 50 ); + +}); + +test( "{ cancel: 'input,textarea,button,select,option' }, default", function() { + expect( 2 ); + + $( "<div id='draggable-option-cancel-default'><input type='text'></div>" ).appendTo( "#main" ); + + var element = $( "#draggable-option-cancel-default" ).draggable({ cancel: "input,textarea,button,select,option" }); + TestHelpers.draggable.shouldMove( element ); + + element.draggable( "destroy" ); + + element = $( "#draggable-option-cancel-default" ).draggable({ cancel: "input,textarea,button,select,option" }); + TestHelpers.draggable.testDrag( element, "#draggable-option-cancel-default input", 50, 50, 0, 0 ); + element.draggable( "destroy" ); +}); + +test( "{ cancel: 'span' }", function() { + expect( 2 ); + + var element = $( "#draggable2" ).draggable(); + TestHelpers.draggable.testDrag( element, "#draggable2 span", 50, 50, 50, 50 ); + + element.draggable( "destroy" ); + + element = $( "#draggable2" ).draggable({ cancel: "span" }); + TestHelpers.draggable.testDrag( element, "#draggable2 span", 50, 50, 0, 0 ); +}); + +test( "{ cancel: ? }, unexpected", function() { + expect( 6 ); + + var element, + unexpected = { + "true": true, + "false": false, + "{}": {}, + "[]": [], + "null": null, + "undefined": undefined + }; + + $.each( unexpected, function( key, val ) { + element = $( "#draggable2" ).draggable({ cancel: val }); + TestHelpers.draggable.shouldMove( element, "cancel: " + key ); + element.draggable( "destroy" ); + }); +}); + +/** +test( "{ cancel: Selectors }, matching parent selector", function() { + + expect( 5 ); + + var element = $( "#draggable2" ).draggable({ cancel: "span a" }); + + $( "#qunit-fixture" ).append( "<span id='wrapping'><a></a></span>" ); + + element.find( "span" ).append( "<a>" ); + + $( "#wrapping a" ).append( element ); + + TestHelpers.draggable.testDrag( element, "#draggable2 span", 50, 50, 50, 50, "drag span child" ); + TestHelpers.draggable.shouldNotMove( $( "#draggable2 span a" ) ); + TestHelpers.draggable.shouldNotMove( $( "#wrapping a" ) ); + + $( "#draggable2" ).draggable( "option", "cancel", "span > a" ); + $( "#draggable2" ).find( "a" ).append( "<a>" ); + + + TestHelpers.draggable.testDrag( element, $( "#draggable2 span a" ).last(), 50, 50, 50, 50, "drag span child" ); + TestHelpers.draggable.shouldNotMove( $( "#draggable2 span a" ).first() ); + +}); +*/ + +test( "cancelement, default, switching after initialization", function() { + expect( 3 ); + + $( "<div id='draggable-option-cancel-default'><input type='text'></div>" ).appendTo( "#main" ); + + var input = $( "#draggable-option-cancel-default input" ), + element = $( "#draggable-option-cancel-default" ).draggable(); + + TestHelpers.draggable.testDrag( element, input, 50, 50, 0, 0 ); + + element.draggable( "option", "cancel", "textarea" ); + TestHelpers.draggable.testDrag( element, input, 50, 50, 50, 50 ); + + element.draggable( "option", "cancel", "input" ); + TestHelpers.draggable.testDrag( element, input, 50, 50, 0, 0 ); +}); + +/* + +test( "{ connectToSortable: selector }, default", function() { + expect( 1 ); + + ok(false, "missing test - untested code is broken code" ); +}); +*/ + +test( "{ containment: Element }", function() { + expect( 1 ); + + var offsetAfter, + element = $( "#draggable1" ).draggable({ containment: $( "#draggable1" ).parent()[ 0 ] }), + p = element.parent(), + po = p.offset(), + expected = { + left: po.left + TestHelpers.draggable.border( p, "left" ) + TestHelpers.draggable.margin( element, "left" ), + top: po.top + TestHelpers.draggable.border( p, "top" ) + TestHelpers.draggable.margin( element, "top" ) + }; + + element.simulate( "drag", { + dx: -100, + dy: -100 + }); + offsetAfter = element.offset(); + deepEqual( offsetAfter, expected, "compare offset to parent" ); +}); + +test( "{ containment: Selector }", function() { + expect( 1 ); + + var offsetAfter, + element = $( "#draggable1" ).draggable({ containment: $( "#qunit-fixture" ) }), + p = element.parent(), + po = p.offset(), + expected = { + left: po.left + TestHelpers.draggable.border( p, "left" ) + TestHelpers.draggable.margin( element, "left" ), + top: po.top + TestHelpers.draggable.border( p, "top" ) + TestHelpers.draggable.margin( element, "top" ) + }; + + element.simulate( "drag", { + dx: -100, + dy: -100 + }); + offsetAfter = element.offset(); + deepEqual( offsetAfter, expected, "compare offset to parent" ); +}); + +test( "{ containment: [x1, y1, x2, y2] }", function() { + expect( 1 ); + + var element = $( "#draggable1" ).draggable(), + eo = element.offset(); + + element.draggable( "option", "containment", [ eo.left, eo.top, eo.left + element.width() + 5, eo.top + element.height() + 5 ] ); + + TestHelpers.draggable.testDrag( element, element, -100, -100, 0, 0 ); +}); + +test( "{ containment: 'parent' }, relative", function() { + expect( 1 ); + + var offsetAfter, + element = $( "#draggable1" ).draggable({ containment: "parent" }), + p = element.parent(), + po = p.offset(), + expected = { + left: po.left + TestHelpers.draggable.border( p, "left" ) + TestHelpers.draggable.margin( element, "left" ), + top: po.top + TestHelpers.draggable.border( p, "top" ) + TestHelpers.draggable.margin( element, "top" ) + }; + + element.simulate( "drag", { + dx: -100, + dy: -100 + }); + offsetAfter = element.offset(); + deepEqual( offsetAfter, expected, "compare offset to parent" ); +}); + +test( "{ containment: 'parent' }, absolute", function() { + expect( 1 ); + + var offsetAfter, + element = $( "#draggable2" ).draggable({ containment: "parent" }), + p = element.parent(), + po = p.offset(), + expected = { + left: po.left + TestHelpers.draggable.border( p, "left" ) + TestHelpers.draggable.margin( element, "left" ), + top: po.top + TestHelpers.draggable.border( p, "top" ) + TestHelpers.draggable.margin( element, "top" ) + }; + + element.simulate( "drag", { + dx: -100, + dy: -100 + }); + offsetAfter = element.offset(); + deepEqual( offsetAfter, expected, "compare offset to parent" ); +}); + +test( "containment, account for border", function() { + expect( 2 ); + + var el = $("#draggable1").appendTo("#main"), + parent = el.parent().css({ + height: "100px", + width: "100px", + borderStyle: "solid", + borderWidth: "5px 10px 15px 20px" + }), + parentBottom = parent.offset().top + parent.outerHeight(), + parentRight = parent.offset().left + parent.outerWidth(), + parentBorderBottom = TestHelpers.draggable.border( parent, "bottom" ), + parentBorderRight = TestHelpers.draggable.border( parent, "right" ); + + el.css({ + height: "5px", + width: "5px" + }).draggable({ containment: "parent" }); + + el.simulate( "drag", { + dx: 100, + dy: 100 + }); + + equal( el.offset().top, parentBottom - parentBorderBottom - el.height(), + "The draggable should be on top of its parent's bottom border" ); + equal( el.offset().left, parentRight - parentBorderRight - el.width(), + "The draggable should be to the right of its parent's right border" ); +}); + +test( "containment, default, switching after initialization", function() { + expect( 3 ); + + var element = $( "#draggable1" ).draggable({ containment: false }); + + TestHelpers.draggable.testDrag( element, element, -100, -100, -100, -100 ); + + element.draggable( "option", "containment", "parent" ) + .css({ + top: 0, + left: 0 + }) + .appendTo( $( "#main" ) ); + + TestHelpers.draggable.testDrag( element, element, -100, -100, 0, 0 ); + + element.draggable( "option", "containment", false ); + TestHelpers.draggable.testDrag( element, element, -100, -100, -100, -100 ); +}); + +test( "{ cursor: 'auto' }, default", function() { + function getCursor() { + return $( "#draggable2" ).css( "cursor" ); + } + + expect( 2 ); + + var actual, after, + expected = "auto", + element = $( "#draggable2" ).draggable({ + cursor: expected, + start: function() { + actual = getCursor(); + } + }), + before = getCursor(); + + element.simulate( "drag", { + dx: -1, + dy: -1 + }); + after = getCursor(); + + equal( actual, expected, "start callback: cursor '" + expected + "'" ); + equal( after, before, "after drag: cursor restored" ); +}); + +test( "{ cursor: 'move' }", function() { + function getCursor() { + return $( "body" ).css( "cursor" ); + } + + expect( 2 ); + + var actual, after, + expected = "move", + element = $( "#draggable2" ).draggable({ + cursor: expected, + start: function() { + actual = getCursor(); + } + }), + before = getCursor(); + + element.simulate( "drag", { + dx: -1, + dy: -1 + }); + after = getCursor(); + + equal( actual, expected, "start callback: cursor '" + expected + "'" ); + equal( after, before, "after drag: cursor restored" ); +}); + +test( "cursor, default, switching after initialization", function() { + expect( 3 ); + + var element = $( "#draggable1" ).draggable(); + + TestHelpers.draggable.trackMouseCss( element ); + + TestHelpers.draggable.move( element, 1, 1 ); + equal( element.data( "last_dragged_cursor" ), "auto" ); + + element.draggable( "option", "cursor", "move" ); + TestHelpers.draggable.move( element, 1, 1 ); + equal( element.data( "last_dragged_cursor" ), "move" ); + + element.draggable( "option", "cursor", "ns-resize" ); + TestHelpers.draggable.move( element, 1, 1 ); + equal( element.data( "last_dragged_cursor" ), "ns-resize" ); +}); + +test( "cursorAt", function() { + expect( 24 ); + + var deltaX = -3, + deltaY = -3, + tests = { + "false": { cursorAt : false }, + "{ left: -5, top: -5 }": { x: -5, y: -5, cursorAt : { left: -5, top: -5 } }, + "[ 10, 20 ]": { x: 10, y: 20, cursorAt : [ 10, 20 ] }, + "'10 20'": { x: 10, y: 20, cursorAt : "10 20" }, + "{ left: 20, top: 40 }": { x: 20, y: 40, cursorAt : { left: 20, top: 40 } }, + "{ right: 10, bottom: 20 }": { x: 10, y: 20, cursorAt : { right: 10, bottom: 20 } } + }; + + $.each( tests, function( testName, testData ) { + $.each( [ "relative", "absolute" ], function( i, position ) { + var element = $( "#draggable" + ( i + 1 ) ).draggable({ + cursorAt: testData.cursorAt, + drag: function( event, ui ) { + if( !testData.cursorAt ) { + equal( ui.position.left - ui.originalPosition.left, deltaX, testName + " " + position + " left" ); + equal( ui.position.top - ui.originalPosition.top, deltaY, testName + " " + position + " top" ); + } else if( testData.cursorAt.right ) { + equal( ui.helper.width() - ( event.clientX - ui.offset.left ), testData.x - TestHelpers.draggable.unreliableOffset, testName + " " + position + " left" ); + equal( ui.helper.height() - ( event.clientY - ui.offset.top ), testData.y - TestHelpers.draggable.unreliableOffset, testName + " " +position + " top" ); + } else { + equal( event.clientX - ui.offset.left, testData.x + TestHelpers.draggable.unreliableOffset, testName + " " + position + " left" ); + equal( event.clientY - ui.offset.top, testData.y + TestHelpers.draggable.unreliableOffset, testName + " " + position + " top" ); + } + } + }); + + element.simulate( "drag", { + moves: 1, + dx: deltaX, + dy: deltaY + }); + }); + }); +}); + +test( "cursorAt, switching after initialization", function() { + expect( 24 ); + + var deltaX = -3, + deltaY = -3, + tests = { + "false": { cursorAt : false }, + "{ left: -5, top: -5 }": { x: -5, y: -5, cursorAt : { left: -5, top: -5 } }, + "[ 10, 20 ]": { x: 10, y: 20, cursorAt : [ 10, 20 ] }, + "'10 20'": { x: 10, y: 20, cursorAt : "10 20" }, + "{ left: 20, top: 40 }": { x: 20, y: 40, cursorAt : { left: 20, top: 40 } }, + "{ right: 10, bottom: 20 }": { x: 10, y: 20, cursorAt : { right: 10, bottom: 20 } } + }; + + $.each( tests, function( testName, testData ) { + $.each( [ "relative", "absolute" ], function( i, position ) { + var element = $( "#draggable" + ( i + 1 ) ); + + element.draggable({ + drag: function( event, ui ) { + if( !testData.cursorAt ) { + equal( ui.position.left - ui.originalPosition.left, deltaX, testName + " " + position + " left" ); + equal( ui.position.top - ui.originalPosition.top, deltaY, testName + " " + position + " top" ); + } else if( testData.cursorAt.right ) { + equal( ui.helper.width() - ( event.clientX - ui.offset.left ), testData.x - TestHelpers.draggable.unreliableOffset, testName + " " + position + " left" ); + equal( ui.helper.height() - ( event.clientY - ui.offset.top ), testData.y - TestHelpers.draggable.unreliableOffset, testName + " " +position + " top" ); + } else { + equal( event.clientX - ui.offset.left, testData.x + TestHelpers.draggable.unreliableOffset, testName + " " + position + " left" ); + equal( event.clientY - ui.offset.top, testData.y + TestHelpers.draggable.unreliableOffset, testName + " " + position + " top" ); + } + } + }); + + element.draggable( "option", "cursorAt", false ); + element.draggable( "option", "cursorAt", testData.cursorAt ); + + element.simulate( "drag", { + moves: 1, + dx: deltaX, + dy: deltaY + }); + }); + }); +}); + +test( "disabled", function() { + expect( 3 ); + + var element = $( "#draggable1" ).draggable(); + + TestHelpers.draggable.shouldMove( element ); + + element.draggable( "option", "disabled", true ); + TestHelpers.draggable.shouldNotMove( element ); + + element.draggable( "option", "disabled", false ); + TestHelpers.draggable.shouldMove( element ); +}); + +test( "{ grid: [50, 50] }, relative", function() { + expect( 2 ); + + var element = $( "#draggable1" ).draggable({ grid: [ 50, 50 ] }); + TestHelpers.draggable.testDrag( element, element, 24, 24, 0, 0 ); + TestHelpers.draggable.testDrag( element, element, 26, 25, 50, 50 ); +}); + +test( "{ grid: [50, 50] }, absolute", function() { + expect( 2 ); + + var element = $( "#draggable2" ).draggable({ grid: [ 50, 50 ] }); + TestHelpers.draggable.testDrag( element, element, 24, 24, 0, 0 ); + TestHelpers.draggable.testDrag( element, element, 26, 25, 50, 50 ); +}); + +test( "grid, switching after initialization", function() { + expect( 4 ); + + var element = $( "#draggable1" ).draggable(); + + // Forward + TestHelpers.draggable.testDrag( element, element, 24, 24, 24, 24 ); + TestHelpers.draggable.testDrag( element, element, 0, 0, 0, 0 ); + + element.draggable( "option", "grid", [ 50,50 ] ); + + TestHelpers.draggable.testDrag( element, element, 24, 24, 0, 0 ); + TestHelpers.draggable.testDrag( element, element, 26, 25, 50, 50 ); +}); + +test( "{ handle: 'span' }", function() { + expect( 3 ); + + var element = $( "#draggable2" ).draggable({ handle: "span" }); + + TestHelpers.draggable.testDrag( element, "#draggable2 span", 50, 50, 50, 50, "drag span" ); + TestHelpers.draggable.testDrag( element, "#draggable2 span em", 50, 50, 50, 50, "drag span child" ); + TestHelpers.draggable.shouldNotMove( element, "drag element" ); +}); + +test( "handle, default, switching after initialization", function() { + expect( 6 ); + + var element = $( "#draggable2" ).draggable(); + + TestHelpers.draggable.testDrag( element, element, 50, 50, 50, 50 ); + TestHelpers.draggable.testDrag( element, "#draggable2 span", 100, 100, 100, 100 ); + + // Switch + element.draggable( "option", "handle", "span" ); + TestHelpers.draggable.testDrag( element, element, 50, 50, 0, 0 ); + TestHelpers.draggable.testDrag( element, "#draggable2 span", 100, 100, 100, 100 ); + + // And back + element.draggable( "option", "handle", false ); + TestHelpers.draggable.testDrag( element, element, 50, 50, 50, 50 ); + TestHelpers.draggable.testDrag( element, "#draggable2 span", 100, 100, 100, 100 ); +}); + +test( "helper, default, switching after initialization", function() { + expect( 3 ); + + var element = $( "#draggable1" ).draggable(); + TestHelpers.draggable.shouldMove( element ); + + element.draggable( "option", "helper", "clone" ); + TestHelpers.draggable.shouldNotMove( element ); + + element.draggable( "option", "helper", "original" ); + TestHelpers.draggable.shouldMove( element ); +}); + +test( "{ helper: 'clone' }, relative", function() { + expect( 1 ); + + var element = $( "#draggable1" ).draggable({ helper: "clone" }); + TestHelpers.draggable.shouldNotMove( element ); +}); + +test( "{ helper: 'clone' }, absolute", function() { + expect( 1 ); + + var element = $( "#draggable2" ).draggable({ helper: "clone" }); + TestHelpers.draggable.shouldNotMove( element ); +}); + +test( "{ helper: 'original' }, relative, with scroll offset on parent", function() { + expect( 3 ); + + var element = $( "#draggable1" ).draggable({ helper: "original" }); + + TestHelpers.draggable.setScroll(); + TestHelpers.draggable.testScroll( element, "relative" ); + + TestHelpers.draggable.setScroll(); + TestHelpers.draggable.testScroll( element, "static" ); + + TestHelpers.draggable.setScroll(); + TestHelpers.draggable.testScroll( element, "absolute" ); + + TestHelpers.draggable.restoreScroll(); +}); + +test( "{ helper: 'original' }, relative, with scroll offset on root", function() { + expect( 3 ); + + var element = $( "#draggable1" ).draggable({ helper: "original" }); + + TestHelpers.draggable.setScroll( "root" ); + TestHelpers.draggable.testScroll( element, "relative" ); + + TestHelpers.draggable.setScroll( "root" ); + TestHelpers.draggable.testScroll( element, "static" ); + + TestHelpers.draggable.setScroll( "root" ); + TestHelpers.draggable.testScroll( element, "absolute" ); + + TestHelpers.draggable.restoreScroll( "root" ); +}); + +test( "{ helper: 'original' }, relative, with scroll offset on root and parent", function() { + expect( 3 ); + + var element = $( "#draggable1" ).draggable({ helper: "original" }); + + TestHelpers.draggable.setScroll(); + TestHelpers.draggable.setScroll( "root" ); + TestHelpers.draggable.testScroll( element, "relative" ); + + TestHelpers.draggable.setScroll(); + TestHelpers.draggable.setScroll( "root" ); + TestHelpers.draggable.testScroll( element, "static" ); + + TestHelpers.draggable.setScroll(); + TestHelpers.draggable.setScroll( "root" ); + TestHelpers.draggable.testScroll( element, "absolute" ); + + TestHelpers.draggable.restoreScroll(); + TestHelpers.draggable.restoreScroll( "root" ); +}); + +test( "{ helper: 'original' }, absolute, with scroll offset on parent", function() { + expect( 3 ); + + var element = $( "#draggable1" ).css({ position: "absolute", top: 0, left: 0 }).draggable({ helper: "original" }); + + TestHelpers.draggable.setScroll(); + TestHelpers.draggable.testScroll( element, "relative" ); + + TestHelpers.draggable.setScroll(); + TestHelpers.draggable.testScroll( element, "static" ); + + TestHelpers.draggable.setScroll(); + TestHelpers.draggable.testScroll( element, "absolute" ); + + TestHelpers.draggable.restoreScroll(); +}); + +test( "{ helper: 'original' }, absolute, with scroll offset on root", function() { + expect( 3 ); + + var element = $( "#draggable1" ).css({ position: "absolute", top: 0, left: 0 }).draggable({ helper: "original" }); + + TestHelpers.draggable.setScroll( "root" ); + TestHelpers.draggable.testScroll( element, "relative" ); + + TestHelpers.draggable.setScroll( "root" ); + TestHelpers.draggable.testScroll( element, "static" ); + + TestHelpers.draggable.setScroll( "root" ); + TestHelpers.draggable.testScroll( element, "absolute" ); + + TestHelpers.draggable.restoreScroll( "root" ); +}); + +test( "{ helper: 'original' }, absolute, with scroll offset on root and parent", function() { + expect( 3 ); + + var element = $( "#draggable1" ).css({ position: "absolute", top: 0, left: 0 }).draggable({ helper: "original" }); + + TestHelpers.draggable.setScroll(); + TestHelpers.draggable.setScroll( "root" ); + TestHelpers.draggable.testScroll( element, "relative" ); + + TestHelpers.draggable.setScroll(); + TestHelpers.draggable.setScroll( "root" ); + TestHelpers.draggable.testScroll( element, "static" ); + + TestHelpers.draggable.setScroll(); + TestHelpers.draggable.setScroll( "root" ); + TestHelpers.draggable.testScroll( element, "absolute" ); + + TestHelpers.draggable.restoreScroll(); + TestHelpers.draggable.restoreScroll( "root" ); +}); + +test( "{ helper: 'original' }, fixed, with scroll offset on parent", function() { + expect( 3 ); + + var element = $( "#draggable1" ).css({ position: "fixed", top: 0, left: 0 }).draggable({ helper: "original" }); + + TestHelpers.draggable.setScroll(); + TestHelpers.draggable.testScroll( element, "relative" ); + + TestHelpers.draggable.setScroll(); + TestHelpers.draggable.testScroll( element, "static" ); + + TestHelpers.draggable.setScroll(); + TestHelpers.draggable.testScroll( element, "absolute" ); + + TestHelpers.draggable.restoreScroll(); +}); + +test( "{ helper: 'original' }, fixed, with scroll offset on root", function() { + expect( 3 ); + + var element = $( "#draggable1" ).css({ position: "fixed", top: 0, left: 0 }).draggable({ helper: "original" }); + + TestHelpers.draggable.setScroll( "root" ); + TestHelpers.draggable.testScroll( element, "relative" ); + + TestHelpers.draggable.setScroll( "root" ); + TestHelpers.draggable.testScroll( element, "static" ); + + TestHelpers.draggable.setScroll( "root" ); + TestHelpers.draggable.testScroll( element, "absolute" ); + + TestHelpers.draggable.restoreScroll( "root" ); +}); + +test( "{ helper: 'original' }, fixed, with scroll offset on root and parent", function() { + expect( 3 ); + + var element = $( "#draggable1" ).css({ position: "fixed", top: 0, left: 0 }).draggable({ helper: "original" }); + + TestHelpers.draggable.setScroll(); + TestHelpers.draggable.setScroll( "root" ); + TestHelpers.draggable.testScroll( element, "relative" ); + + TestHelpers.draggable.setScroll(); + TestHelpers.draggable.setScroll( "root" ); + TestHelpers.draggable.testScroll( element, "static" ); + + TestHelpers.draggable.setScroll(); + TestHelpers.draggable.setScroll( "root" ); + TestHelpers.draggable.testScroll( element, "absolute" ); + + TestHelpers.draggable.restoreScroll(); + TestHelpers.draggable.restoreScroll( "root" ); +}); + +test( "{ helper: 'clone' }, absolute", function() { + expect( 1 ); + + var helperOffset = null, + origOffset = $( "#draggable1" ).offset(), + element = $( "#draggable1" ).draggable({ helper: "clone", drag: function( event, ui) { + helperOffset = ui.helper.offset(); + } }); + + element.simulate( "drag", { + dx: 1, + dy: 1 + }); + deepEqual({ top: helperOffset.top - 1, left: helperOffset.left - 1 }, origOffset, "dragged[1, 1]" ); +}); + +test( "{ helper: 'clone' }, absolute with scroll offset on parent", function() { + expect( 3 ); + + TestHelpers.draggable.setScroll(); + var helperOffset = null, + origOffset = null, + element = $( "#draggable1" ).draggable({ + helper: "clone", + drag: function( event, ui) { + helperOffset = ui.helper.offset(); + } + }); + + $( "#main" ).css( "position", "relative" ); + origOffset = $( "#draggable1" ).offset(); + element.simulate( "drag", { + dx: 1, + dy: 1 + }); + deepEqual({ top: helperOffset.top - 1, left: helperOffset.left - 1 }, origOffset, "dragged[1, 1]" ); + + $( "#main" ).css( "position", "static" ); + origOffset = $( "#draggable1" ).offset(); + element.simulate( "drag", { + dx: 1, + dy: 1 + }); + deepEqual({ top: helperOffset.top - 1, left: helperOffset.left - 1 }, origOffset, "dragged[1, 1]" ); + + $( "#main" ).css( "position", "absolute" ); + origOffset = $( "#draggable1" ).offset(); + element.simulate( "drag", { + dx: 1, + dy: 1 + }); + deepEqual({ top: helperOffset.top - 1, left: helperOffset.left - 1 }, origOffset, "dragged[1, 1]" ); + + TestHelpers.draggable.restoreScroll(); +}); + +test( "{ helper: 'clone' }, absolute with scroll offset on root", function() { + expect( 3 ); + + TestHelpers.draggable.setScroll( "root" ); + var helperOffset = null, + origOffset = null, + element = $( "#draggable1" ).draggable({ + helper: "clone", + drag: function( event, ui) { + helperOffset = ui.helper.offset(); + } + }); + + $( "#main" ).css( "position", "relative" ); + origOffset = $( "#draggable1" ).offset(); + element.simulate( "drag", { + dx: 1, + dy: 1 + }); + deepEqual({ top: helperOffset.top - 1, left: helperOffset.left - 1 }, origOffset, "dragged[1, 1]" ); + + $( "#main" ).css( "position", "static" ); + origOffset = $( "#draggable1" ).offset(); + element.simulate( "drag", { + dx: 1, + dy: 1 + }); + deepEqual({ top: helperOffset.top - 1, left: helperOffset.left - 1 }, origOffset, "dragged[1, 1]" ); + + $( "#main" ).css( "position", "absolute" ); + origOffset = $( "#draggable1" ).offset(); + element.simulate( "drag", { + dx: 1, + dy: 1 + }); + deepEqual({ top: helperOffset.top - 1, left: helperOffset.left - 1 }, origOffset, "dragged[1, 1]" ); + + TestHelpers.draggable.restoreScroll( "root" ); +}); + +test( "{ helper: 'clone' }, absolute with scroll offset on root and parent", function() { + expect( 3 ); + + TestHelpers.draggable.setScroll( "root" ); + TestHelpers.draggable.setScroll(); + + var helperOffset = null, + origOffset = null, + element = $( "#draggable1" ).draggable({ + helper: "clone", + drag: function( event, ui) { + helperOffset = ui.helper.offset(); + } + }); + + $( "#main" ).css( "position", "relative" ); + origOffset = $( "#draggable1" ).offset(); + element.simulate( "drag", { + dx: 1, + dy: 1 + }); + deepEqual({ top: helperOffset.top - 1, left: helperOffset.left - 1 }, origOffset, "dragged[1, 1]" ); + + $( "#main" ).css( "position", "static" ); + origOffset = $( "#draggable1" ).offset(); + element.simulate( "drag", { + dx: 1, + dy: 1 + }); + deepEqual({ top: helperOffset.top - 1, left: helperOffset.left - 1 }, origOffset, "dragged[1, 1]" ); + + $( "#main" ).css( "position", "absolute" ); + origOffset = $( "#draggable1" ).offset(); + element.simulate( "drag", { + dx: 1, + dy: 1 + }); + deepEqual({ top: helperOffset.top - 1, left: helperOffset.left - 1 }, origOffset, "dragged[1, 1]" ); + + TestHelpers.draggable.restoreScroll( "root" ); + TestHelpers.draggable.restoreScroll(); +}); + +test( "{ opacity: 0.5 }", function() { + expect( 1 ); + + var opacity = null, + element = $( "#draggable2" ).draggable({ + opacity: 0.5, + start: function() { + opacity = $(this).css( "opacity" ); + } + }); + + element.simulate( "drag", { + dx: -1, + dy: -1 + }); + + equal( opacity, 0.5, "start callback: opacity is" ); +}); + +test( "opacity, default, switching after initialization", function() { + expect( 3 ); + + var opacity = null, + element = $( "#draggable2" ).draggable({ + start: function() { + opacity = $(this).css( "opacity" ); + } + }); + + TestHelpers.draggable.move( element, 1, 1 ); + equal( opacity, 1 ); + + element.draggable( "option", "opacity", 0.5 ); + TestHelpers.draggable.move( element, 2, 1 ); + equal( opacity, 0.5 ); + + element.draggable( "option", "opacity", false ); + TestHelpers.draggable.move( element, 3, 1 ); + equal( opacity, 1 ); +}); + +asyncTest( "revert and revertDuration", function() { + expect( 4 ); + + var element = $( "#draggable2" ).draggable({ + revert: true, + revertDuration: 0 + }); + TestHelpers.draggable.shouldNotMove( element, "revert: true, revertDuration: 0 should revert immediately" ); + + $( "#draggable2" ).draggable( "option", "revert", "invalid" ); + TestHelpers.draggable.shouldNotMove( element, "revert: invalid, revertDuration: 0 should revert immediately" ); + + $( "#draggable2" ).draggable( "option", "revert", false ); + TestHelpers.draggable.shouldMove( element, "revert: false should allow movement" ); + + $( "#draggable2" ).draggable( "option", { + revert: true, + revertDuration: 200, + stop: function() { + start(); + } + }); + + // animation are async, so test for it asynchronously + TestHelpers.draggable.move( element, 50, 50 ); + setTimeout( function() { + ok( $( "#draggable2" ).is( ":animated" ), "revert: true with revertDuration should animate" ); + }); +}); + +test( "revert: valid", function() { + expect( 1 ); + + var element = $( "#draggable2" ).draggable({ + revert: "valid", + revertDuration: 0 + }); + + $( "#droppable" ).droppable(); + + TestHelpers.draggable.testDrag( element, element, 100, 100, 0, 0, "revert: valid reverts when dropped on a droppable" ); +}); + +test( "scope", function() { + expect( 2 ); + + var element = $( "#draggable2" ).draggable({ + scope: "tasks", + revert: "valid", + revertDuration: 0 + }); + + $( "#droppable" ).droppable({ scope: "tasks" }); + + TestHelpers.draggable.testDrag( element, element, 100, 100, 0, 0, "revert: valid reverts when dropped on a droppable in scope" ); + + $( "#droppable" ).droppable( "destroy" ).droppable({ scope: "nottasks" }); + + TestHelpers.draggable.testDrag( element, element, 100, 100, 100, 100, "revert: valid reverts when dropped on a droppable out of scope" ); +}); + +test( "scroll, scrollSensitivity, and scrollSpeed", function() { + expect( 2 ); + + var viewportHeight = $( window ).height(), + element = $( "#draggable1" ).draggable({ scroll: true }), + scrollSensitivity = element.draggable( "option", "scrollSensitivity" ), + scrollSpeed = element.draggable( "option", "scrollSpeed" ); + + element.offset({ + top: viewportHeight - scrollSensitivity - 1, + left: 1 + }); + + element.simulate( "drag", { + dx: 1, + y: viewportHeight - scrollSensitivity - 1, + moves: 1 + }); + + ok( $( window ).scrollTop() === 0, "scroll: true doesn't scroll when the element is dragged outside of scrollSensitivity" ); + + element.draggable( "option", "scrollSensitivity", scrollSensitivity + 10 ); + + element.offset({ + top: viewportHeight - scrollSensitivity - 1, + left: 1 + }); + + element.simulate( "drag", { + dx: 1, + y: viewportHeight - scrollSensitivity - 1, + moves: 1 + }); + + ok( $( window ).scrollTop() === scrollSpeed, "scroll: true scrolls when the element is dragged within scrollSensitivity" ); + + TestHelpers.draggable.restoreScroll( document ); +}); + +test( "#6817: auto scroll goes double distance when dragging", function() { + expect( 2 ); + + var offsetBefore, + distance = 10, + viewportHeight = $( window ).height(), + element = $( "#draggable1" ).draggable({ + scroll: true, + stop: function( e, ui ) { + equal( ui.offset.top, newY, "offset of item matches pointer position after scroll" ); + // TODO: fix IE8 testswarm IFRAME positioning bug so closeEnough can be turned back to equal + closeEnough( ui.offset.top - offsetBefore.top, distance, 1, "offset of item only moves expected distance after scroll" ); + } + }), + scrollSensitivity = element.draggable( "option", "scrollSensitivity" ), + oldY = viewportHeight - scrollSensitivity, + newY = oldY + distance; + + element.offset({ + top: oldY, + left: 1 + }); + + offsetBefore = element.offset(); + + element.simulate( "drag", { + handle: "corner", + dx: 1, + y: newY, + moves: 1 + }); + + TestHelpers.draggable.restoreScroll( document ); +}); + +test( "snap, snapMode, and snapTolerance", function() { + expect( 10 ); + + var newX, newY, + snapTolerance = 15, + element = $( "#draggable1" ).draggable({ + snap: true, + snapMode: "both", + snapTolerance: snapTolerance + }), + element2 = $( "#draggable2" ).draggable(); + + element.offset({ + top: 1, + left: 1 + }); + + newX = element2.offset().left - element.outerWidth() - snapTolerance - 2; + newY = element2.offset().top; + + element.simulate( "drag", { + handle: "corner", + x: newX, + y: newY, + moves: 1 + }); + + // TODO: fix IE8 testswarm IFRAME positioning bug so closeEnough can be turned back to equal + closeEnough( element.offset().left, newX, 1, "doesn't snap outside the snapTolerance" ); + closeEnough( element.offset().top, newY, 1, "doesn't snap outside the snapTolerance" ); + + newX += 3; + + element.simulate( "drag", { + handle: "corner", + x: newX, + y: newY, + moves: 1 + }); + + notDeepEqual( element.offset(), { top: newY, left: newX }, "snaps inside the snapTolerance" ); + + element.draggable( "option", "snap", "#draggable2" ); + + element.simulate( "drag", { + handle: "corner", + x: newX, + y: newY, + moves: 1 + }); + + notDeepEqual( element.offset(), { top: newY, left: newX }, "snaps based on selector" ); + + element.draggable( "option", "snap", "#draggable3" ); + + element.simulate( "drag", { + handle: "corner", + x: newX, + y: newY, + moves: 1 + }); + + deepEqual( element.offset(), { top: newY, left: newX }, "doesn't snap based on invalid selector" ); + + element.draggable( "option", "snap", true ); + element.draggable( "option", "snapTolerance", snapTolerance - 2 ); + element.simulate( "drag", { + handle: "corner", + x: newX, + y: newY, + moves: 1 + }); + + deepEqual( element.offset(), { top: newY, left: newX }, "doesn't snap outside the modified snapTolerance" ); + + element.draggable( "option", "snapTolerance", snapTolerance ); + element.draggable( "option", "snapMode", "inner" ); + + element.simulate( "drag", { + handle: "corner", + x: newX, + y: newY, + moves: 1 + }); + + deepEqual( element.offset(), { top: newY, left: newX }, "doesn't snap inside the outer snapTolerance area when snapMode is inner" ); + + newX = element2.offset().left - snapTolerance - 1; + newY = element2.offset().top; + + element.simulate( "drag", { + handle: "corner", + x: newX, + y: newY, + moves: 1 + }); + + deepEqual( element.offset(), { top: newY, left: newX }, "doesn't snap inside the outer snapTolerance area when snapMode is inner" ); + + newX++; + + element.simulate( "drag", { + handle: "corner", + x: newX, + y: newY, + moves: 1 + }); + + notDeepEqual( element.offset(), { top: newY, left: newX }, "snaps inside the inner snapTolerance area when snapMode is inner" ); + + element.draggable( "option", "snapMode", "outer" ); + + element.simulate( "drag", { + handle: "corner", + x: newX, + y: newY, + moves: 1 + }); + + deepEqual( element.offset(), { top: newY, left: newX }, "doesn't snap on the inner snapTolerance area when snapMode is outer" ); +}); + +test( "#8459: element can snap to an element that was removed during drag", function() { + expect( 2 ); + + var newX, newY, + snapTolerance = 15, + element = $( "#draggable1" ).draggable({ + snap: true, + snapMode: "both", + snapTolerance: snapTolerance, + start: function() { + element2.remove(); + } + }), + element2 = $( "#draggable2" ).draggable(); + + element.offset({ + top: 1, + left: 1 + }); + + newX = element2.offset().left - element.outerWidth() - snapTolerance + 1; + newY = element2.offset().top; + + element.simulate( "drag", { + handle: "corner", + x: newX, + y: newY, + moves: 1 + }); + + // Support: Opera 12.10, Safari 5.1, jQuery <1.8 + if ( TestHelpers.draggable.unreliableContains ) { + ok( true, "Opera <12.14 and Safari <6.0 report wrong values for $.contains in jQuery < 1.8" ); + ok( true, "Opera <12.14 and Safari <6.0 report wrong values for $.contains in jQuery < 1.8" ); + } else { + // TODO: fix IE8 testswarm IFRAME positioning bug so closeEnough can be turned back to equal + closeEnough( element.offset().left, newX, 1, "doesn't snap to a removed element" ); + closeEnough( element.offset().top, newY, 1, "doesn't snap to a removed element" ); + } +}); + +test( "#8165: Snapping large rectangles to small rectangles doesn't snap properly", function() { + expect( 1 ); + + var snapTolerance = 20, + y = 1, + element = $( "#draggable1" ) + .css({ + width: "50px", + height: "200px" + }).offset({ + top: y, + left: 1 + }), + element2 = $( "#draggable2" ) + .css({ + width: "50px", + height: "50px" + }).offset({ + top: y + snapTolerance + 1, + left: 200 + }), + newX = element2.offset().left - element.outerWidth() - snapTolerance + 1; + + $( "#draggable1, #draggable2" ).draggable({ + snap: true, + snapTolerance: snapTolerance + }); + + element.simulate( "drag", { + handle: "corner", + x: newX, + moves: 1 + }); + + notDeepEqual( element.offset(), { top: y, left: newX }, "snaps even if only a side (not a corner) is inside the snapTolerance" ); +}); + +test( "stack", function() { + expect( 2 ); + + var element = $( "#draggable1" ).draggable({ + stack: "#draggable1, #draggable2" + }), + element2 = $( "#draggable2" ).draggable({ + stack: "#draggable1, #draggable2" + }); + + TestHelpers.draggable.move( element, 1, 1 ); + equal( element.css( "zIndex" ), "2", "stack increments zIndex correctly" ); + + TestHelpers.draggable.move( element2, 1, 1 ); + equal( element2.css( "zIndex" ), "3", "stack increments zIndex correctly" ); +}); + +test( "{ zIndex: 10 }", function() { + expect( 1 ); + + var actual, + expected = 10, + element = $( "#draggable2" ).draggable({ + zIndex: expected, + start: function() { + actual = $(this).css( "zIndex" ); + } + }); + + element.simulate( "drag", { + dx: -1, + dy: -1 + }); + + equal( actual, expected, "start callback: zIndex is" ); + +}); + +test( "zIndex, default, switching after initialization", function() { + + expect( 3 ); + + var zindex = null, + element = $( "#draggable2" ).draggable({ + start: function() { + zindex = $(this).css( "z-index" ); + } + }); + + element.css( "z-index", 1 ); + + TestHelpers.draggable.move( element, 1, 1 ); + equal( zindex, 1 ); + + element.draggable( "option", "zIndex", 5 ); + TestHelpers.draggable.move( element, 2, 1 ); + equal( zindex, 5 ); + + element.draggable( "option", "zIndex", false ); + TestHelpers.draggable.move( element, 3, 1 ); + equal( zindex, 1 ); + +}); + +})( jQuery ); diff --git a/apps/it/static/js/ui/tests/unit/draggable/draggable_test_helpers.js b/apps/it/static/js/ui/tests/unit/draggable/draggable_test_helpers.js new file mode 100644 index 0000000..b365055 --- /dev/null +++ b/apps/it/static/js/ui/tests/unit/draggable/draggable_test_helpers.js @@ -0,0 +1,79 @@ +TestHelpers.draggable = { + // TODO: remove the unreliable offset hacks + unreliableOffset: $.ui.ie && ( !document.documentMode || document.documentMode < 8 ) ? 2 : 0, + // Support: Opera 12.10, Safari 5.1, jQuery <1.8 + unreliableContains: (function(){ + var element = $( "<div>" ); + return $.contains( element[ 0 ].ownerDocument, element[ 0 ] ); + })(), + testDrag: function( el, handle, dx, dy, expectedDX, expectedDY, msg ) { + var offsetAfter, actual, expected, + offsetBefore = el.offset(); + + $( handle ).simulate( "drag", { + dx: dx, + dy: dy + }); + offsetAfter = el.offset(); + + actual = { left: offsetAfter.left, top: offsetAfter.top }; + expected = { left: offsetBefore.left + expectedDX, top: offsetBefore.top + expectedDY }; + + msg = msg ? msg + "." : ""; + deepEqual( actual, expected, "dragged[" + dx + ", " + dy + "] " + msg ); + }, + shouldMove: function( el, why ) { + TestHelpers.draggable.testDrag( el, el, 50, 50, 50, 50, why ); + }, + shouldNotMove: function( el, why ) { + TestHelpers.draggable.testDrag( el, el, 50, 50, 0, 0, why ); + }, + testScroll: function( el, position ) { + var oldPosition = $( "#main" ).css( "position" ); + $( "#main" ).css( "position", position); + TestHelpers.draggable.shouldMove( el, position + " parent" ); + $( "#main" ).css( "position", oldPosition ); + }, + restoreScroll: function( what ) { + if( what ) { + $( document ).scrollTop( 0 ).scrollLeft( 0 ); + } else { + $( "#main" ).scrollTop( 0 ).scrollLeft( 0 ); + } + }, + setScroll: function( what ) { + if( what ) { + // TODO: currently, the draggable interaction doesn't properly account for scrolled pages, + // uncomment the line below to make the tests fail that should when the page is scrolled + // $( document ).scrollTop( 100 ).scrollLeft( 100 ); + } else { + $( "#main" ).scrollTop( 100 ).scrollLeft( 100 ); + } + }, + border: function( el, side ) { + return parseInt( el.css( "border-" + side + "-width" ), 10 ) || 0; + }, + margin: function( el, side ) { + return parseInt( el.css( "margin-" + side ), 10 ) || 0; + }, + move: function( el, x, y ) { + $( el ).simulate( "drag", { + dx: x, + dy: y + }); + }, + trackMouseCss : function( el ) { + el.bind( "drag", function() { + el.data( "last_dragged_cursor", $( "body" ).css( "cursor" ) ); + }); + }, + trackAppendedParent : function( el ) { + // TODO: appendTo is currently ignored if helper is original (see #7044) + el.draggable( "option", "helper", "clone" ); + + // Get what parent is at time of drag + el.bind( "drag", function(e,ui) { + el.data( "last_dragged_parent", ui.helper.parent()[ 0 ] ); + }); + } +};
\ No newline at end of file diff --git a/apps/it/static/js/ui/tests/unit/droppable/all.html b/apps/it/static/js/ui/tests/unit/droppable/all.html new file mode 100644 index 0000000..9a50050 --- /dev/null +++ b/apps/it/static/js/ui/tests/unit/droppable/all.html @@ -0,0 +1,30 @@ +<!doctype html> +<html lang="en"> +<head> + <meta charset="utf-8"> + <title>jQuery UI Droppable Test Suite</title> + + <script src="../../../jquery-1.10.2.js"></script> + + <link rel="stylesheet" href="../../../external/qunit.css"> + <link rel="stylesheet" href="../qunit-composite.css"> + <script src="../../../external/qunit.js"></script> + <script src="../qunit-composite.js"></script> + <script src="../subsuite.js"></script> + + <script> + testAllVersions( "droppable" ); + </script> +</head> +<body> + +<h1 id="qunit-header">jQuery UI Droppable Test Suite</h1> +<h2 id="qunit-banner"></h2> +<div id="qunit-testrunner-toolbar"></div> +<h2 id="qunit-userAgent"></h2> +<ol id="qunit-tests"></ol> +<div id="qunit-fixture"> + +</div> +</body> +</html> diff --git a/apps/it/static/js/ui/tests/unit/droppable/droppable.html b/apps/it/static/js/ui/tests/unit/droppable/droppable.html new file mode 100644 index 0000000..d084464 --- /dev/null +++ b/apps/it/static/js/ui/tests/unit/droppable/droppable.html @@ -0,0 +1,50 @@ +<!doctype html> +<html lang="en"> +<head> + <meta charset="utf-8"> + <title>jQuery UI Droppable Test Suite</title> + + <script src="../../jquery.js"></script> + <link rel="stylesheet" href="../../../external/qunit.css"> + <script src="../../../external/qunit.js"></script> + <script src="../../jquery.simulate.js"></script> + <script src="../testsuite.js"></script> + <script> + TestHelpers.loadResources({ + css: [ "ui.core" ], + js: [ + "ui/jquery.ui.core.js", + "ui/jquery.ui.widget.js", + "ui/jquery.ui.mouse.js", + "ui/jquery.ui.draggable.js", + "ui/jquery.ui.droppable.js" + ] + }); + </script> + + <script src="droppable_common.js"></script> + <script src="droppable_core.js"></script> + <script src="droppable_events.js"></script> + <script src="droppable_methods.js"></script> + <script src="droppable_options.js"></script> + <script src="droppable_test_helpers.js"></script> + + <script src="../swarminject.js"></script> +</head> +<body> + +<h1 id="qunit-header">jQuery UI Droppable Test Suite</h1> +<h2 id="qunit-banner"></h2> +<div id="qunit-testrunner-toolbar"></div> +<h2 id="qunit-userAgent"></h2> +<ol id="qunit-tests"></ol> +<div id="qunit-fixture"> + +<div id="draggable1" style="width: 25px; height: 25px;">Draggable</div> +<div id="droppable1" style="width: 100px; height: 100px;">Droppable</div> +<div id="droppable2" style="width: 100px; height: 100px;">Droppable</div> +<div style='width:1000px;height:1000px;'> </div> + +</div> +</body> +</html> diff --git a/apps/it/static/js/ui/tests/unit/droppable/droppable_common.js b/apps/it/static/js/ui/tests/unit/droppable/droppable_common.js new file mode 100644 index 0000000..c112def --- /dev/null +++ b/apps/it/static/js/ui/tests/unit/droppable/droppable_common.js @@ -0,0 +1,20 @@ +TestHelpers.commonWidgetTests( "droppable", { + defaults: { + accept: "*", + activeClass: false, + addClasses: true, + disabled: false, + greedy: false, + hoverClass: false, + scope: "default", + tolerance: "intersect", + + // callbacks + activate: null, + create: null, + deactivate: null, + drop: null, + out: null, + over: null + } +}); diff --git a/apps/it/static/js/ui/tests/unit/droppable/droppable_core.js b/apps/it/static/js/ui/tests/unit/droppable/droppable_core.js new file mode 100644 index 0000000..53b08fd --- /dev/null +++ b/apps/it/static/js/ui/tests/unit/droppable/droppable_core.js @@ -0,0 +1,29 @@ +/* + * droppable_core.js + */ + +(function($) { + +module("droppable: core"); + +test("element types", function() { + var typeNames = ("p,h1,h2,h3,h4,h5,h6,blockquote,ol,ul,dl,div,form" + + ",table,fieldset,address,ins,del,em,strong,q,cite,dfn,abbr" + + ",acronym,code,samp,kbd,var,img,hr" + + ",input,button,label,select,iframe").split(","); + + expect( typeNames.length ); + + $.each(typeNames, function(i) { + var typeName = typeNames[i], + el = $(document.createElement(typeName)).appendTo("body"); + + (typeName === "table" && el.append("<tr><td>content</td></tr>")); + el.droppable(); + TestHelpers.droppable.shouldDrop(); + el.droppable("destroy"); + el.remove(); + }); +}); + +})(jQuery); diff --git a/apps/it/static/js/ui/tests/unit/droppable/droppable_events.js b/apps/it/static/js/ui/tests/unit/droppable/droppable_events.js new file mode 100644 index 0000000..4b8fe5a --- /dev/null +++ b/apps/it/static/js/ui/tests/unit/droppable/droppable_events.js @@ -0,0 +1,63 @@ +(function( $ ) { + +module( "droppable: events" ); + +test( "droppable destruction/recreation on drop event", function() { + expect( 1 ); + + var config = { + activeClass: "active", + drop: function() { + var element = $( this ), + newDroppable = $( "<div>" ) + .css({ width: 100, height: 100 }) + .text( "Droppable" ); + element.after( newDroppable ); + element.remove(); + newDroppable.droppable( config ); + } + }, + + draggable = $( "#draggable1" ).draggable(), + droppable1 = $( "#droppable1" ).droppable( config ), + droppable2 = $( "#droppable2" ).droppable( config ), + + droppableOffset = droppable1.offset(), + draggableOffset = draggable.offset(), + dx = droppableOffset.left - draggableOffset.left, + dy = droppableOffset.top - draggableOffset.top; + + draggable.simulate( "drag", { + dx: dx, + dy: dy + }); + + ok( !droppable2.hasClass( "active" ), "subsequent droppable no longer active" ); +}); + + + +// todo: comment the following in when ready to actually test +/* +test("activate", function() { + ok(false, 'missing test - untested code is broken code'); +}); + +test("deactivate", function() { + ok(false, 'missing test - untested code is broken code'); +}); + +test("over", function() { + ok(false, 'missing test - untested code is broken code'); +}); + +test("out", function() { + ok(false, 'missing test - untested code is broken code'); +}); + +test("drop", function() { + ok(false, 'missing test - untested code is broken code'); +}); +*/ + +})( jQuery ); diff --git a/apps/it/static/js/ui/tests/unit/droppable/droppable_methods.js b/apps/it/static/js/ui/tests/unit/droppable/droppable_methods.js new file mode 100644 index 0000000..2b452a6 --- /dev/null +++ b/apps/it/static/js/ui/tests/unit/droppable/droppable_methods.js @@ -0,0 +1,88 @@ +/* + * droppable_methods.js + */ +(function($) { + +module("droppable: methods"); + +test("init", function() { + expect( 5 ); + + $("<div></div>").appendTo("body").droppable().remove(); + ok(true, ".droppable() called on element"); + + $([]).droppable(); + ok(true, ".droppable() called on empty collection"); + + $("<div></div>").droppable(); + ok(true, ".droppable() called on disconnected DOMElement"); + + $("<div></div>").droppable().droppable("option", "foo"); + ok(true, "arbitrary option getter after init"); + + $("<div></div>").droppable().droppable("option", "foo", "bar"); + ok(true, "arbitrary option setter after init"); +}); + +test("destroy", function() { + expect( 4 ); + + $("<div></div>").appendTo("body").droppable().droppable("destroy").remove(); + ok(true, ".droppable('destroy') called on element"); + + $([]).droppable().droppable("destroy"); + ok(true, ".droppable('destroy') called on empty collection"); + + $("<div></div>").droppable().droppable("destroy"); + ok(true, ".droppable('destroy') called on disconnected DOMElement"); + + var expected = $("<div></div>").droppable(), + actual = expected.droppable("destroy"); + equal(actual, expected, "destroy is chainable"); +}); + +test("enable", function() { + expect(7); + + var el, expected, actual; + + el = $("#droppable1").droppable({ disabled: true }); + TestHelpers.droppable.shouldNotDrop(); + el.droppable("enable"); + TestHelpers.droppable.shouldDrop(); + equal(el.droppable("option", "disabled"), false, "disabled option getter"); + el.droppable("destroy"); + el.droppable({ disabled: true }); + TestHelpers.droppable.shouldNotDrop(); + el.droppable("option", "disabled", false); + equal(el.droppable("option", "disabled"), false, "disabled option setter"); + TestHelpers.droppable.shouldDrop(); + + expected = $("<div></div>").droppable(), + actual = expected.droppable("enable"); + equal(actual, expected, "enable is chainable"); +}); + +test("disable", function() { + expect(7); + + var el, actual, expected; + + el = $("#droppable1").droppable({ disabled: false }); + TestHelpers.droppable.shouldDrop(); + el.droppable("disable"); + TestHelpers.droppable.shouldNotDrop(); + equal(el.droppable("option", "disabled"), true, "disabled option getter"); + el.droppable("destroy"); + el.droppable({ disabled: false }); + TestHelpers.droppable.shouldDrop(); + el.droppable("option", "disabled", true); + equal(el.droppable("option", "disabled"), true, "disabled option setter"); + TestHelpers.droppable.shouldNotDrop(); + + expected = $("<div></div>").droppable(), + actual = expected.droppable("disable"); + equal(actual, expected, "disable is chainable"); +}); + +})(jQuery); diff --git a/apps/it/static/js/ui/tests/unit/droppable/droppable_options.js b/apps/it/static/js/ui/tests/unit/droppable/droppable_options.js new file mode 100644 index 0000000..c2ecdcf --- /dev/null +++ b/apps/it/static/js/ui/tests/unit/droppable/droppable_options.js @@ -0,0 +1,67 @@ +/* + * droppable_options.js + */ +(function($) { + +module("droppable: options"); + +/* +test("{ accept '*' }, default ", function() { + ok(false, 'missing test - untested code is broken code'); +}); + +test("{ accept: Selector }", function() { + ok(false, 'missing test - untested code is broken code'); +}); + +test("{ accept: function(draggable) }", function() { + ok(false, 'missing test - untested code is broken code'); +}); + +test("activeClass", function() { + ok(false, 'missing test - untested code is broken code'); +}); +*/ +test("{ addClasses: true }, default", function() { + expect( 1 ); + var el = $("<div></div>").droppable({ addClasses: true }); + ok(el.is(".ui-droppable"), "'ui-droppable' class added"); + el.droppable("destroy"); +}); + +test("{ addClasses: false }", function() { + expect( 1 ); + var el = $("<div></div>").droppable({ addClasses: false }); + ok(!el.is(".ui-droppable"), "'ui-droppable' class not added"); + el.droppable("destroy"); +}); +/* +test("greedy", function() { + ok(false, 'missing test - untested code is broken code'); +}); + +test("hoverClass", function() { + ok(false, 'missing test - untested code is broken code'); +}); + +test("scope", function() { + ok(false, 'missing test - untested code is broken code'); +}); + +test("tolerance, fit", function() { + ok(false, 'missing test - untested code is broken code'); +}); + +test("tolerance, intersect", function() { + ok(false, 'missing test - untested code is broken code'); +}); + +test("tolerance, pointer", function() { + ok(false, 'missing test - untested code is broken code'); +}); + +test("tolerance, touch", function() { + ok(false, 'missing test - untested code is broken code'); +}); +*/ +})(jQuery); diff --git a/apps/it/static/js/ui/tests/unit/droppable/droppable_test_helpers.js b/apps/it/static/js/ui/tests/unit/droppable/droppable_test_helpers.js new file mode 100644 index 0000000..ffd3745 --- /dev/null +++ b/apps/it/static/js/ui/tests/unit/droppable/droppable_test_helpers.js @@ -0,0 +1,10 @@ +TestHelpers.droppable = { + shouldDrop: function() { + // todo: actually implement this + ok(true, "missing test - untested code is broken code"); + }, + shouldNotDrop: function() { + // todo: actually implement this + ok(true, "missing test - untested code is broken code"); + } +};
\ No newline at end of file diff --git a/apps/it/static/js/ui/tests/unit/effects/all.html b/apps/it/static/js/ui/tests/unit/effects/all.html new file mode 100644 index 0000000..7448586 --- /dev/null +++ b/apps/it/static/js/ui/tests/unit/effects/all.html @@ -0,0 +1,30 @@ +<!doctype html> +<html lang="en"> +<head> + <meta charset="utf-8"> + <title>jQuery UI Effects Test Suite</title> + + <script src="../../../jquery-1.10.2.js"></script> + + <link rel="stylesheet" href="../../../external/qunit.css"> + <link rel="stylesheet" href="../qunit-composite.css"> + <script src="../../../external/qunit.js"></script> + <script src="../qunit-composite.js"></script> + <script src="../subsuite.js"></script> + + <script> + testAllVersions( "effects" ); + </script> +</head> +<body> + +<h1 id="qunit-header">jQuery UI Effects Test Suite</h1> +<h2 id="qunit-banner"></h2> +<div id="qunit-testrunner-toolbar"></div> +<h2 id="qunit-userAgent"></h2> +<ol id="qunit-tests"></ol> +<div id="qunit-fixture"> + +</div> +</body> +</html> diff --git a/apps/it/static/js/ui/tests/unit/effects/effects.html b/apps/it/static/js/ui/tests/unit/effects/effects.html new file mode 100644 index 0000000..4538ecb --- /dev/null +++ b/apps/it/static/js/ui/tests/unit/effects/effects.html @@ -0,0 +1,117 @@ +<!doctype html> +<html lang="en"> +<head> + <meta charset="utf-8"> + <title>jQuery UI Effects Test Suite</title> + + <script src="../../jquery.js"></script> + <link rel="stylesheet" href="../../../external/qunit.css"> + <script src="../../../external/qunit.js"></script> + <script src="../../jquery.simulate.js"></script> + <script src="../testsuite.js"></script> + <script> + TestHelpers.loadResources({ + js: [ + "ui/jquery.ui.effect.js", + "ui/jquery.ui.effect-blind.js", + "ui/jquery.ui.effect-bounce.js", + "ui/jquery.ui.effect-clip.js", + "ui/jquery.ui.effect-drop.js", + "ui/jquery.ui.effect-explode.js", + "ui/jquery.ui.effect-fade.js", + "ui/jquery.ui.effect-fold.js", + "ui/jquery.ui.effect-highlight.js", + "ui/jquery.ui.effect-pulsate.js", + "ui/jquery.ui.effect-scale.js", + "ui/jquery.ui.effect-shake.js", + "ui/jquery.ui.effect-slide.js", + "ui/jquery.ui.effect-transfer.js" + ] + }); + </script> + + <script src="effects_core.js"></script> + <script src="effects_scale.js"></script> + + <script src="../swarminject.js"></script> + <style> + #qunit-fixture { + width: 1000px; + height: 1000px; + } + .hidden { + display: none; + } + .test { + background: #000; + border: 0; + width: 100px; + height: 100px; + } + .testAddBorder { + border: 10px solid #000; + } + .testChildren, + .testChangeBackground { + background: #fff; + } + .test h2 { + font-size: 10px; + } + .testChildren h2 { + font-size: 20px; + } + + .relWidth { + width: 50%; + } + + .relHeight { + height: 50%; + } + + .testScale { + border: 5px solid #000; + padding: 5px; + margin: 5px; + width: 50px; + height: 50px; + } + + .ticket7106 { + width: 50px; + height: 50px; + } + .ticket7106.animate { + width: 100px; + } + + </style> +</head> +<body> + +<h1 id="qunit-header">jQuery UI Effects Test Suite</h1> +<h2 id="qunit-banner"></h2> +<div id="qunit-testrunner-toolbar"></div> +<h2 id="qunit-userAgent"></h2> +<ol id="qunit-tests"></ol> +<div id="qunit-fixture"> +<div id="elem" class="test"> +</div> +<div class="hidden test"> + <div>.</div> +</div> +<div class="animateClass test"> + <h2>Child Element Test</h2> +</div> +<div class="relWidth relHeight testAddBorder"> + <h2>Slide with relative width</h2> +</div> +<div class="testScale"> +</div> +<div class="ticket7106"> +</div> + +</div> +</body> +</html> diff --git a/apps/it/static/js/ui/tests/unit/effects/effects_core.js b/apps/it/static/js/ui/tests/unit/effects/effects_core.js new file mode 100644 index 0000000..11e9d0b --- /dev/null +++ b/apps/it/static/js/ui/tests/unit/effects/effects_core.js @@ -0,0 +1,274 @@ +(function($) { + +function present( value, array, message ) { + QUnit.push( jQuery.inArray( value, array ) !== -1 , value, array, message ); +} + +function notPresent( value, array, message ) { + QUnit.push( jQuery.inArray( value, array ) === -1 , value, array, message ); +} + +// minDuration is used for "short" animate tests where we are only concerned about the final +var minDuration = 15, + + // duration is used for "long" animates where we plan on testing properties during animation + duration = 200; + +module( "effects.core" ); + +// TODO: test all signatures of .show(), .hide(), .toggle(). +// Look at core's signatures and UI's signatures. +asyncTest( ".hide() with step", function() { + expect( 1 ); + var element = $( "#elem" ), + step = function() { + ok( true, "step callback invoked" ); + step = $.noop; + }; + + element.hide({ + step: function() { + step(); + }, + complete: start + }); +}); + +test( "Immediate Return Conditions", function() { + var hidden = $( "div.hidden" ), + count = 0; + expect( 3 ); + hidden.hide( "blind", function() { + equal( ++count, 1, "Hide on hidden returned immediately" ); + }).show().show( "blind", function() { + equal( ++count, 2, "Show on shown returned immediately" ); + }); + equal( ++count, 3, "Both Functions worked properly" ); +}); + +test( ".hide() with hidden parent", function() { + expect( 1 ); + var element = $( "div.hidden" ).children(); + element.hide( "blind", function() { + equal( element.css( "display" ), "none", "display: none" ); + }); +}); + +asyncTest( "Parse of null for options", function() { + var hidden = $( "div.hidden" ), + count = 0; + expect( 1 ); + hidden.show( "blind", null, 1, function() { + equal( ++count, 1, "null for options still works" ); + start(); + }); +}); + +test( "removeClass", function() { + expect( 3 ); + + var element = $( "<div>" ); + equal( "", element[ 0 ].className ); + element.addClass( "destroyed" ); + equal( "destroyed", element[ 0 ].className ); + element.removeClass(); + equal( "", element[ 0 ].className ); +}); + + +/* TODO: Disabled - Can't figure out why this is failing in IE 6/7 +test( "createWrapper and removeWrapper retain focused elements (#7595)", function() { + expect( 2 ); + var test = $( "div.hidden" ).show(), + input = $( "<input type='text'>" ).appendTo( test ).focus(); + + $.effects.createWrapper( test ); + equal( document.activeElement, input[ 0 ], "Active element is still input after createWrapper" ); + $.effects.removeWrapper( test ); + equal( document.activeElement, input[ 0 ], "Active element is still input after removeWrapper" ); +}); +*/ + +module( "effects.core: animateClass" ); + +asyncTest( "animateClass works with borderStyle", function() { + var test = $("div.animateClass"); + expect(3); + test.toggleClass("testAddBorder", minDuration, function() { + test.toggleClass("testAddBorder", minDuration, function() { + equal( test.css("borderLeftStyle"), "none", "None border set" ); + start(); + }); + equal( test.css("borderLeftStyle"), "solid", "None border not immedately set" ); + }); + equal( test.css("borderLeftStyle"), "solid", "Solid border immedately set" ); +}); + +asyncTest( "animateClass works with colors", function() { + var test = $("div.animateClass"), + oldStep = jQuery.fx.step.backgroundColor; + + expect(2); + + // we want to catch the first frame of animation + jQuery.fx.step.backgroundColor = function( fx ) { + oldStep.apply( this, arguments ); + + // make sure it has animated somewhere we can detect + if ( fx.pos > 255 / 2000 ) { + jQuery.fx.step.backgroundColor = oldStep; + notPresent( test.css("backgroundColor"), + [ "#000000", "#ffffff", "#000", "#fff", "rgb(0, 0, 0)", "rgb(255,255,255)" ], + "Color is not endpoints in middle." ); + test.stop( true, true ); + } + }; + + test.toggleClass("testChangeBackground", { + duration: 2000, + complete: function() { + present( test.css("backgroundColor"), [ "#ffffff", "#fff", "rgb(255, 255, 255)" ], "Color is final" ); + start(); + } + }); +}); + +asyncTest( "animateClass calls step option", 1, function() { + var test = jQuery( "div.animateClass" ), + step = function() { + ok( true, "Step Function Called" ); + test.stop(); + start(); + step = $.noop; + }; + test.toggleClass( "testChangeBackground", { + step: function() { + step(); + } + }); +}); + +asyncTest( "animateClass works with children", 3, function() { + var animatedChild, + test = $("div.animateClass"), + h2 = test.find("h2"); + + test.toggleClass("testChildren", { + children: true, + duration: duration, + complete: function() { + equal( h2.css("fontSize"), "20px", "Text size is final during complete"); + test.toggleClass("testChildren", { + duration: duration, + complete: function() { + equal( h2.css("fontSize"), "10px", "Text size revertted after class removed"); + + start(); + }, + step: function( val, fx ) { + if ( fx.elem === h2[ 0 ] ) { + ok( false, "Error - Animating property on h2" ); + } + } + }); + }, + step: function( val, fx ) { + if ( fx.prop === "fontSize" && fx.elem === h2[ 0 ] && !animatedChild ) { + equal( fx.end, 20, "animating font size on child" ); + animatedChild = true; + } + } + }); +}); + +asyncTest( "animateClass clears style properties when stopped", function() { + var test = $("div.animateClass"), + style = test[0].style, + orig = style.cssText; + + expect( 2 ); + + test.addClass( "testChangeBackground", duration ); + notEqual( orig, style.cssText, "cssText is not the same after starting animation" ); + + test.stop( true, true ); + equal( orig, $.trim( style.cssText ), "cssText is the same after stopping animation midway" ); + start(); +}); + +asyncTest( "animateClass: css and class changes during animation are not lost (#7106)", function() { + expect( 2 ); + var test = $( "div.ticket7106" ); + + // ensure the class stays and that the css property stays + function animationComplete() { + ok( test.hasClass( "testClass" ), "class change during animateClass was not lost" ); + equal( test.height(), 100, "css change during animateClass was not lost" ); + start(); + } + + // add a class and change a style property after starting an animated class + test.addClass( "animate", minDuration, animationComplete ) + .addClass( "testClass" ) + .height( 100 ); +}); + + +$.each( $.effects.effect, function( effect ) { + module( "effects." + effect ); + + // puff and size are defined inside scale + if ( effect !== "puff" && effect !== "size" ) { + TestHelpers.testJshint( "effect-" + effect ); + } + + if ( effect === "transfer" ) { + return; + } + asyncTest( "show/hide", function() { + expect( 8 ); + var hidden = $( "div.hidden" ), + count = 0, + test = 0; + + function queueTest( fn ) { + count++; + var point = count; + return function( next ) { + test++; + equal( point, test, "Queue function fired in order" ); + if ( fn ) { + fn(); + } else { + setTimeout( next, minDuration ); + } + }; + } + + hidden.queue( queueTest() ).show( effect, minDuration, queueTest(function() { + equal( hidden.css("display"), "block", "Hidden is shown after .show(\"" +effect+ "\", time)" ); + })).queue( queueTest() ).hide( effect, minDuration, queueTest(function() { + equal( hidden.css("display"), "none", "Back to hidden after .hide(\"" +effect+ "\", time)" ); + })).queue( queueTest(function() { + deepEqual( hidden.queue(), ["inprogress"], "Only the inprogress sentinel remains"); + start(); + })); + }); + + asyncTest( "relative width & height - properties are preserved", function() { + var test = $("div.relWidth.relHeight"), + width = test.width(), height = test.height(), + cssWidth = test[0].style.width, cssHeight = test[0].style.height; + + expect( 4 ); + test.toggle( effect, minDuration, function() { + equal( test[0].style.width, cssWidth, "Inline CSS Width has been reset after animation ended" ); + equal( test[0].style.height, cssHeight, "Inline CSS Height has been rest after animation ended" ); + start(); + }); + equal( test.width(), width, "Width is the same px after animation started" ); + equal( test.height(), height, "Height is the same px after animation started" ); + }); +}); + +})(jQuery); diff --git a/apps/it/static/js/ui/tests/unit/effects/effects_scale.js b/apps/it/static/js/ui/tests/unit/effects/effects_scale.js new file mode 100644 index 0000000..6abbcb5 --- /dev/null +++ b/apps/it/static/js/ui/tests/unit/effects/effects_scale.js @@ -0,0 +1,68 @@ +(function( $ ) { +module( "effect.scale: Scale" ); + +function run( position, v, h, vo, ho ) { + var desc = "End Position Correct: " + position + " (" + v + "," + h + ") - origin: (" + vo + "," + ho + ")"; + asyncTest( desc, function() { + expect( 2 ); + function complete() { + equal( parseInt( test.css( h ), 10 ), target[ h ], "Horizontal Position Correct " + desc ); + equal( parseInt( test.css( v ), 10 ), target[ v ], "Vertical Position Correct " + desc ); + start(); + } + var test = $( ".testScale" ), + css = { + position: position + }, + effect = { + effect: "scale", + mode: "effect", + percent: 200, + origin: [ vo, ho ], + complete: complete, + duration: 1 + }, + target = {}, + relative = position === "relative"; + + css[ h ] = 33; + css[ v ] = 33; + target[ h ] = h === ho ? css[ h ] : ho === "center" ? css[ h ] - 35 : css[ h ] - 70; + target[ v ] = v === vo ? css[ v ] : vo === "middle" ? css[ v ] - 35 : css[ v ] - 70; + if ( relative && h === "right" ) { + target[ h ] += 70; + } + if ( relative && v === "bottom" ) { + target[ v ] += 70; + } + test.css( css ); + test.effect( effect ); + }); +} + +function suite( position ) { + run( position, "top", "left", "top", "left" ); + run( position, "top", "left", "middle", "center" ); + run( position, "top", "left", "bottom", "right" ); + /* Firefox is currently not capable of supporting detection of bottom and right.... + run( position, "bottom", "right", "top", "left" ); + run( position, "bottom", "right", "middle", "center" ); + run( position, "bottom", "right", "bottom", "right" ); + */ +} + +$(function() { + suite( "absolute" ); + suite( "relative" ); + var fixedElem = $( "<div>" ) + .css({ + position: "fixed", + top: 10 + }) + .appendTo( "body" ); + if ( fixedElem.offset().top === 10 ) { + suite( "fixed" ); + } +}); + +})( jQuery ); diff --git a/apps/it/static/js/ui/tests/unit/images/jqueryui_32x32.png b/apps/it/static/js/ui/tests/unit/images/jqueryui_32x32.png Binary files differnew file mode 100644 index 0000000..e003d16 --- /dev/null +++ b/apps/it/static/js/ui/tests/unit/images/jqueryui_32x32.png diff --git a/apps/it/static/js/ui/tests/unit/index.html b/apps/it/static/js/ui/tests/unit/index.html new file mode 100644 index 0000000..68784d1 --- /dev/null +++ b/apps/it/static/js/ui/tests/unit/index.html @@ -0,0 +1,66 @@ +<!doctype html> +<html lang="en"> +<head> + <meta charset="utf-8"> + <title>jQuery UI Unit Tests</title> + + <link rel="stylesheet" href="../../themes/base/jquery.ui.core.css"> + <link rel="stylesheet" href="../../themes/base/jquery.ui.theme.css"> + <link rel="stylesheet" href="../index.css"> + <script src="../jquery-1.10.2.js"></script> + <script src="../index.js"></script> +</head> +<body> + +<div id="main"> + <h1>jQuery UI Unit Tests</h1> + <div> + <h2>Full Test Suite</h2> + <ul> + <li><a href="all.html">All</a></li> + </ul> + + <h2>Core</h2> + <ul> + <li><a href="core/core.html">Core</a></li> + <li><a href="widget/widget.html">Widget</a></li> + </ul> + + <h2>Interactions</h2> + <ul> + <li><a href="draggable/draggable.html">Draggable</a></li> + <li><a href="droppable/droppable.html">Droppable</a></li> + <li><a href="resizable/resizable.html">Resizable</a></li> + <li><a href="selectable/selectable.html">Selectable</a></li> + <li><a href="sortable/sortable.html">Sortable</a></li> + </ul> + + <h2>Widgets</h2> + <ul> + <li><a href="accordion/accordion.html">Accordion</a></li> + <li><a href="autocomplete/autocomplete.html">Autocomplete</a></li> + <li><a href="button/button.html">Button</a></li> + <li><a href="datepicker/datepicker.html">Datepicker</a></li> + <li><a href="dialog/dialog.html">Dialog</a></li> + <li><a href="menu/menu.html">Menu</a></li> + <li><a href="progressbar/progressbar.html">Progressbar</a></li> + <li><a href="slider/slider.html">Slider</a></li> + <li><a href="spinner/spinner.html">Spinner</a></li> + <li><a href="tabs/tabs.html">Tabs</a></li> + <li><a href="tooltip/tooltip.html">Tooltip</a></li> + </ul> + + <h2>Utilities</h2> + <ul> + <li><a href="position/position.html">Position</a></li> + </ul> + + <h2>Effects</h2> + <ul> + <li><a href="effects/effects.html">Effects</a></li> + </ul> + </div> +</div> + +</body> +</html> diff --git a/apps/it/static/js/ui/tests/unit/menu/all.html b/apps/it/static/js/ui/tests/unit/menu/all.html new file mode 100644 index 0000000..fb41cd8 --- /dev/null +++ b/apps/it/static/js/ui/tests/unit/menu/all.html @@ -0,0 +1,30 @@ +<!doctype html> +<html lang="en"> +<head> + <meta charset="utf-8"> + <title>jQuery UI Menu Test Suite</title> + + <script src="../../../jquery-1.10.2.js"></script> + + <link rel="stylesheet" href="../../../external/qunit.css"> + <link rel="stylesheet" href="../qunit-composite.css"> + <script src="../../../external/qunit.js"></script> + <script src="../qunit-composite.js"></script> + <script src="../subsuite.js"></script> + + <script> + testAllVersions( "menu" ); + </script> +</head> +<body> + +<h1 id="qunit-header">jQuery UI Menu Test Suite</h1> +<h2 id="qunit-banner"></h2> +<div id="qunit-testrunner-toolbar"></div> +<h2 id="qunit-userAgent"></h2> +<ol id="qunit-tests"></ol> +<div id="qunit-fixture"> + +</div> +</body> +</html> diff --git a/apps/it/static/js/ui/tests/unit/menu/menu.html b/apps/it/static/js/ui/tests/unit/menu/menu.html new file mode 100644 index 0000000..ed37623 --- /dev/null +++ b/apps/it/static/js/ui/tests/unit/menu/menu.html @@ -0,0 +1,269 @@ +<!doctype html> +<html lang="en"> +<head> + <meta charset="utf-8"> + <title>jQuery UI Menu Test Suite</title> + + <script src="../../jquery.js"></script> + <link rel="stylesheet" href="../../../external/qunit.css"> + <script src="../../../external/qunit.js"></script> + <script src="../../jquery.simulate.js"></script> + <script src="../testsuite.js"></script> + <script> + TestHelpers.loadResources({ + css: [ "ui.core", "ui.menu" ], + js: [ + "ui/jquery.ui.core.js", + "ui/jquery.ui.widget.js", + "ui/jquery.ui.menu.js" + ] + }); + </script> + + <script src="menu_test_helpers.js"></script> + <script src="menu_common.js"></script> + <script src="menu_core.js"></script> + <script src="menu_events.js"></script> + <script src="menu_methods.js"></script> + <script src="menu_options.js"></script> + + <script src="../swarminject.js"></script> + <style> + .ui-menu { + font-size: 15px; + } + #menu3 { + height: 250px; + overflow: auto; + } + #menu4, #menu4 ul { + height: 250px; + overflow: auto; + } + </style> +</head> +<body> + +<div id="qunit">jQuery UI Menu Test Suite</div> +<div id="qunit-fixture"> + +<ul class="foo" id="menu1"> + <li class="foo"><a class="foo" href="#">Aberdeen</a></li> + <li class="foo"><a class="foo" href="#">Ada</a></li> + <li class="foo"><a class="foo" href="#">Adamsville</a></li> + <li class="foo"><a id="testID1" class="foo" href="#">Addyston</a></li> + <li class="foo"><a class="foo" href="#">Adelphi</a></li> +</ul> + +<ul id="menu2"> + <li class="foo"><a class="foo" href="#">Aberdeen</a></li> + <li class="foo"><a class="foo" href="#">Ada</a></li> + <li class="foo"><a class="foo" href="#">Adamsville</a></li> + <li class="foo"><a class="foo" href="#"><span class="ui-icon ui-icon-print"></span>Addyston</a></li> + <li> + <a href="#">Delphi</a> + <ul> + <li class="foo"><a class="foo" href="#">Ada</a></li> + <li class="foo"><a class="foo" href="#">Saarland</a></li> + <li class="foo"><a class="foo" href="#">Salzburg</a></li> + </ul> + </li> + <li class="foo"><a class="foo" href="#">Saarland</a></li> + <li> + <a href="#">Salzburg</a> + <ul> + <li> + <a href="#">Delphi</a> + <ul> + <li class="foo"><a class="foo" href="#">Ada</a></li> + <li class="foo"><a class="foo" href="#">Saarland</a></li> + <li class="foo"><a class="foo" href="#">Salzburg</a></li> + </ul> + </li> + <li> + <a href="#">Delphi</a> + <ul> + <li class="foo"><a class="foo" href="#">Ada</a></li> + <li> - </li> + <li class="foo"><a class="foo" href="#">Saarland</a></li> + <li></li> + <li class="foo"><a class="foo" href="#">Salzburg</a></li> + <li>–</li> + </ul> + </li> + <li class="foo"><a class="foo" href="#">Perch</a></li> + </ul> + </li> +</ul> + +<ul class="foo" id="menu3"> + <li class="foo"><a class="foo" href="#">Aberdeen</a></li> + <li class="foo"><a class="foo" href="#">Ada</a></li> + <li class="foo"><a class="foo" href="#">Adamsville</a></li> + <li class="foo"><a class="foo" href="#">Addyston</a></li> + <li class="foo"><a class="foo" href="#">Adelphi</a></li> + <li class="foo"><a class="foo" href="#">Adena</a></li> + <li class="foo"><a class="foo" href="#">Adrian</a></li> + <li class="foo"><a class="foo" href="#">Akron</a></li> + <li class="foo"><a class="foo" href="#">Albany</a></li> + <li class="foo"><a class="foo" href="#">Alexandria</a></li> + <li class="foo"><a class="foo" href="#">Alger</a></li> + <li class="foo"><a class="foo" href="#">Alledonia</a></li> + <li class="foo"><a class="foo" href="#">Alliance</a></li> + <li class="foo"><a class="foo" href="#">Alpha</a></li> + <li class="foo"><a class="foo" href="#">Alvada</a></li> + <li class="foo"><a class="foo" href="#">Alvordton</a></li> + <li class="foo"><a class="foo" href="#">Amanda</a></li> + <li class="foo"><a class="foo" href="#">Amelia</a></li> + <li class="foo"><a class="foo" href="#">Amesville</a></li> + <li class="foo"><a class="foo" href="#">Aberdeen</a></li> + <li class="foo"><a class="foo" href="#">Ada</a></li> + <li class="foo"><a class="foo" href="#">Adamsville</a></li> + <li class="foo"><a class="foo" href="#">Addyston</a></li> + <li class="foo"><a class="foo" href="#">Adelphi</a></li> + <li class="foo"><a class="foo" href="#">Adena</a></li> + <li class="foo"><a class="foo" href="#">Adrian</a></li> + <li class="foo"><a class="foo" href="#">Akron</a></li> + <li class="foo"><a class="foo" href="#">Albany</a></li> + <li class="foo"><a class="foo" href="#">Alexandria</a></li> + <li class="foo"><a class="foo" href="#">Alger</a></li> + <li class="foo"><a class="foo" href="#">Alledonia</a></li> + <li class="foo"><a class="foo" href="#">Alliance</a></li> + <li class="foo"><a class="foo" href="#">Alpha</a></li> + <li class="foo"><a class="foo" href="#">Alvada</a></li> + <li class="foo"><a class="foo" href="#">Alvordton</a></li> + <li class="foo"><a class="foo" href="#">Amanda</a></li> + <li class="foo"><a class="foo" href="#">Amelia</a></li> + <li class="foo"><a class="foo" href="#">Amesville</a></li> +</ul> + +<ul class="foo" id="menu4"> + <li class="foo"><a class="foo" href="#">Aberdeen</a></li> + <li class="foo"> + <a class="foo" href="#">Ada</a> + <ul class="foo"> + <li class="foo"><a class="foo" href="#">Aberdeen</a></li> + <li class="foo"><a class="foo" href="#">Ada</a></li> + <li class="foo"><a class="foo" href="#">Adamsville</a></li> + <li class="foo"><a class="foo" href="#">Addyston</a></li> + <li class="foo"><a class="foo" href="#">Adelphi</a></li> + <li class="foo"><a class="foo" href="#">Adena</a></li> + <li class="foo"><a class="foo" href="#">Adrian</a></li> + <li class="foo"><a class="foo" href="#">Akron</a></li> + <li class="foo"><a class="foo" href="#">Albany</a></li> + <li class="foo"><a class="foo" href="#">Alexandria</a></li> + <li class="foo"><a class="foo" href="#">Alger</a></li> + <li class="foo"><a class="foo" href="#">Alledonia</a></li> + <li class="foo"><a class="foo" href="#">Alliance</a></li> + <li class="foo"><a class="foo" href="#">Alpha</a></li> + <li class="foo"><a class="foo" href="#">Alvada</a></li> + <li class="foo"><a class="foo" href="#">Alvordton</a></li> + <li class="foo"><a class="foo" href="#">Amanda</a></li> + <li class="foo"><a class="foo" href="#">Amelia</a></li> + <li class="foo"><a class="foo" href="#">Amesville</a></li> + <li class="foo"><a class="foo" href="#">Aberdeen</a></li> + <li class="foo"><a class="foo" href="#">Ada</a></li> + <li class="foo"><a class="foo" href="#">Adamsville</a></li> + <li class="foo"><a class="foo" href="#">Addyston</a></li> + <li class="foo"><a class="foo" href="#">Adelphi</a></li> + <li class="foo"><a class="foo" href="#">Adena</a></li> + <li class="foo"><a class="foo" href="#">Adrian</a></li> + <li class="foo"><a class="foo" href="#">Akron</a></li> + <li class="foo"><a class="foo" href="#">Albany</a></li> + </ul> + </li> + <li class="foo"><a class="foo" href="#">Adamsville</a></li> + <li class="foo"><a class="foo" href="#">Addyston</a></li> + <li class="foo"><a class="foo" href="#">Adelphi</a></li> + <li class="foo"><a class="foo" href="#">Adena</a></li> + <li class="foo"><a class="foo" href="#">Adrian</a></li> + <li class="foo"><a class="foo" href="#">Akron</a></li> + <li class="foo"><a class="foo" href="#">Albany</a></li> + <li class="foo"><a class="foo" href="#">Alexandria</a></li> + <li class="foo"><a class="foo" href="#">Alger</a></li> + <li class="foo"><a class="foo" href="#">Alledonia</a></li> + <li class="foo"><a class="foo" href="#">Alliance</a></li> + <li class="foo"><a class="foo" href="#">Alpha</a></li> + <li class="foo"><a class="foo" href="#">Alvada</a></li> + <li class="foo"><a class="foo" href="#">Alvordton</a></li> + <li class="foo"><a class="foo" href="#">Amanda</a></li> + <li class="foo"><a class="foo" href="#">Amelia</a></li> + <li class="foo"><a class="foo" href="#">Amesville</a></li> + <li class="foo"><a class="foo" href="#">Aberdeen</a></li> + <li class="foo"><a class="foo" href="#">Ada</a></li> + <li class="foo"><a class="foo" href="#">Adamsville</a></li> + <li class="foo"><a class="foo" href="#">Addyston</a></li> + <li class="foo"><a class="foo" href="#">Adelphi</a></li> + <li class="foo"><a class="foo" href="#">Adena</a></li> + <li class="foo"><a class="foo" href="#">Adrian</a></li> + <li class="foo"><a class="foo" href="#">Akron</a></li> + <li class="foo"><a class="foo" href="#">Albany</a></li> + <li class="foo"><a class="foo" href="#">Alexandria</a></li> + <li class="foo"><a class="foo" href="#">Alger</a></li> + <li class="foo"><a class="foo" href="#">Alledonia</a></li> + <li class="foo"><a class="foo" href="#">Alliance</a></li> + <li class="foo"><a class="foo" href="#">Alpha</a></li> + <li class="foo"><a class="foo" href="#">Alvada</a></li> + <li class="foo"><a class="foo" href="#">Alvordton</a></li> + <li class="foo"><a class="foo" href="#">Amanda</a></li> + <li class="foo"><a class="foo" href="#">Amelia</a></li> + <li class="foo"><a class="foo" href="#">Amesville</a></li> +</ul> + +<div id="menu5"> + <blockquote><a href="#">Aberdeen</a></blockquote> + <blockquote><a href="#">Ada</a></blockquote> + <blockquote><a href="#">Adamsville</a></blockquote> + <blockquote><a href="#">Addyston</a></blockquote> + <blockquote> + <a href="#">Delphi</a> + <div> + <blockquote><a href="#">Ada</a></blockquote> + <blockquote><a href="#">Saarland</a></blockquote> + <blockquote><a href="#">Salzburg</a></blockquote> + </div> + </blockquote> + <blockquote><a href="#">Saarland</a></blockquote> + <blockquote> + <a href="#">Salzburg</a> + <div> + <blockquote> + <a href="#">Delphi</a> + <div> + <blockquote><a href="#">Ada</a></blockquote> + <blockquote><a id="testID2" href="#">Saarland</a></blockquote> + <blockquote><a href="#">Salzburg</a></blockquote> + </div> + </blockquote> + <blockquote> + <a href="#">Delphi</a> + <div> + <blockquote><a href="#">Ada</a></blockquote> + <blockquote><a href="#">Saarland</a></blockquote> + <blockquote><a href="#">Salzburg</a></blockquote> + </div> + </blockquote> + <blockquote><a href="#">Perch</a></blockquote> + </div> + </blockquote> +</div> + +<ul id="menu6"> + <li class="foo"><a class="foo" href="#">Aberdeen</a></li> + <li class="foo ui-state-disabled"><a class="foo" href="#">Ada</a></li> + <li class="foo"><a class="foo" href="#">Adamsville</a></li> + <li class="foo"><a class="foo" href="#">Addyston</a></li> + <li class="ui-state-disabled"> + <a id="testID3" href="#">Delphi</a> + <ul> + <li class="foo"><a class="foo" href="#">Ada</a></li> + <li class="foo"><a class="foo" href="#">Saarland</a></li> + <li class="foo"><a class="foo" href="#">Salzburg</a></li> + </ul> + </li> + <li class="foo"><a class="foo" href="#">Saarland</a></li> +</ul> + +</div> +</body> +</html> diff --git a/apps/it/static/js/ui/tests/unit/menu/menu_common.js b/apps/it/static/js/ui/tests/unit/menu/menu_common.js new file mode 100644 index 0000000..4a89a94 --- /dev/null +++ b/apps/it/static/js/ui/tests/unit/menu/menu_common.js @@ -0,0 +1,20 @@ +TestHelpers.commonWidgetTests( "menu", { + defaults: { + disabled: false, + icons: { + submenu: "ui-icon-carat-1-e" + }, + menus: "ul", + position: { + my: "left top", + at: "right top" + }, + role: "menu", + + // callbacks + blur: null, + create: null, + focus: null, + select: null + } +}); diff --git a/apps/it/static/js/ui/tests/unit/menu/menu_core.js b/apps/it/static/js/ui/tests/unit/menu/menu_core.js new file mode 100644 index 0000000..923d296 --- /dev/null +++ b/apps/it/static/js/ui/tests/unit/menu/menu_core.js @@ -0,0 +1,48 @@ +(function( $ ) { + +module( "menu: core" ); + +test( "markup structure", function() { + expect( 6 ); + var element = $( "#menu1" ).menu(); + ok( element.hasClass( "ui-menu" ), "main element is .ui-menu" ); + element.children().each(function( index ) { + ok( $( this ).hasClass( "ui-menu-item" ), "child " + index + " is .ui-menu-item" ); + }); +}); + +test( "accessibility", function () { + expect( 4 ); + var element = $( "#menu1" ).menu(); + + equal( element.attr( "role" ), "menu", "main role" ); + ok( !element.attr( "aria-activedescendant" ), "aria-activedescendant not set" ); + + element.menu( "focus", $.Event(), element.children().eq( -2 ) ); + equal( element.attr( "aria-activedescendant" ), "testID1", "aria-activedescendant from existing id" ); + + element.menu( "focus", $.Event(), element.children().eq( 0 ) ); + ok( /^ui-id-\d+$/.test( element.attr( "aria-activedescendant" ) ), "aria-activedescendant from generated id" ); + + // Item roles are tested in the role option tests +}); + +asyncTest( "#9044: Autofocus issue with dialog opened from menu widget", function() { + expect( 1 ); + var element = $( "#menu1" ).menu(); + + $( "<input>", { id: "test9044" } ).appendTo( "body" ); + + $( "#testID1" ).bind( "click", function() { + $( "#test9044" ).focus(); + }); + + TestHelpers.menu.click( element, "3" ); + setTimeout( function() { + equal( document.activeElement.id, "test9044", "Focus was swallowed by menu" ); + $( "#test9044" ).remove(); + start(); + }); +}); + +})( jQuery ); diff --git a/apps/it/static/js/ui/tests/unit/menu/menu_events.js b/apps/it/static/js/ui/tests/unit/menu/menu_events.js new file mode 100644 index 0000000..405300c --- /dev/null +++ b/apps/it/static/js/ui/tests/unit/menu/menu_events.js @@ -0,0 +1,637 @@ +(function( $ ) { + +var log = TestHelpers.menu.log, + logOutput = TestHelpers.menu.logOutput, + click = TestHelpers.menu.click; + +module( "menu: events", { + setup: function() { + TestHelpers.menu.clearLog(); + } +}); + +test( "handle click on menu", function() { + expect( 1 ); + var element = $( "#menu1" ).menu({ + select: function() { + log(); + } + }); + log( "click", true ); + click( element, "1" ); + log( "afterclick" ); + click( element, "2" ); + click( element, "3" ); + click( element, "1" ); + equal( logOutput(), "click,1,afterclick,2,3,1", "Click order not valid." ); +}); + +test( "handle click on custom item menu", function() { + expect( 1 ); + var element = $( "#menu5" ).menu({ + select: function() { + log(); + }, + menus: "div" + }); + log( "click", true ); + click( element, "1" ); + log( "afterclick" ); + click( element, "2" ); + click( element, "3" ); + click( element, "1" ); + equal( logOutput(), "click,1,afterclick,2,3,1", "Click order not valid." ); +}); + +asyncTest( "handle blur", function() { + expect( 1 ); + var blurHandled = false, + element = $( "#menu1" ).menu({ + blur: function( event ) { + // Ignore duplicate blur event fired by IE + if ( !blurHandled ) { + blurHandled = true; + equal( event.type, "menublur", "blur event.type is 'menublur'" ); + } + } + }); + + click( element, "1" ); + setTimeout(function() { + element.blur(); + setTimeout(function() { + start(); + }, 350 ); + }); +}); + +asyncTest( "handle blur via click outside", function() { + expect( 1 ); + var blurHandled = false, + element = $( "#menu1" ).menu({ + blur: function( event ) { + // Ignore duplicate blur event fired by IE + if ( !blurHandled ) { + blurHandled = true; + equal( event.type, "menublur", "blur event.type is 'menublur'" ); + } + } + }); + + click( element, "1" ); + setTimeout(function() { + $( "<a>", { id: "remove"} ).appendTo( "body" ).trigger( "click" ); + setTimeout(function() { + start(); + }, 350 ); + }); +}); + +asyncTest( "handle focus of menu with active item", function() { + expect( 1 ); + var element = $( "#menu1" ).menu({ + focus: function( event ) { + log( $( event.target ).find( ".ui-state-focus" ).parent().index() ); + } + }); + + log( "focus", true ); + element[ 0 ].focus(); + setTimeout(function() { + element.simulate( "keydown", { keyCode: $.ui.keyCode.DOWN } ); + element.simulate( "keydown", { keyCode: $.ui.keyCode.DOWN } ); + element[ 0 ].blur(); + setTimeout(function() { + element[ 0 ].focus(); + setTimeout(function() { + equal( logOutput(), "focus,0,1,2,2", "current active item remains active" ); + start(); + }); + }); + }); +}); + +asyncTest( "handle submenu auto collapse: mouseleave", function() { + expect( 4 ); + var element = $( "#menu2" ).menu(), + event = $.Event( "mouseenter" ); + + function menumouseleave1() { + equal( element.find( "ul[aria-expanded='true']" ).length, 1, "first submenu expanded" ); + element.menu( "focus", event, element.find( "li:nth-child(7) li:first" ) ); + setTimeout( menumouseleave2, 350 ); + } + function menumouseleave2() { + equal( element.find( "ul[aria-expanded='true']" ).length, 2, "second submenu expanded" ); + element.find( "ul[aria-expanded='true']:first" ).trigger( "mouseleave" ); + setTimeout( menumouseleave3, 350 ); + } + function menumouseleave3() { + equal( element.find( "ul[aria-expanded='true']" ).length, 1, "second submenu collapsed" ); + element.trigger( "mouseleave" ); + setTimeout( menumouseleave4, 350 ); + } + function menumouseleave4() { + equal( element.find( "ul[aria-expanded='true']" ).length, 0, "first submenu collapsed" ); + start(); + } + + element.find( "li:nth-child(7)" ).trigger( "mouseenter" ); + setTimeout( menumouseleave1, 350 ); +}); + +asyncTest( "handle submenu auto collapse: mouseleave", function() { + expect( 4 ); + var element = $( "#menu5" ).menu({ menus: "div" }), + event = $.Event( "mouseenter" ); + + function menumouseleave1() { + equal( element.find( "div[aria-expanded='true']" ).length, 1, "first submenu expanded" ); + element.menu( "focus", event, element.find( ":nth-child(7)" ).find( "div" ).eq( 0 ).children().eq( 0 ) ); + setTimeout( menumouseleave2, 350 ); + } + function menumouseleave2() { + equal( element.find( "div[aria-expanded='true']" ).length, 2, "second submenu expanded" ); + element.find( "div[aria-expanded='true']:first" ).trigger( "mouseleave" ); + setTimeout( menumouseleave3, 350 ); + } + function menumouseleave3() { + equal( element.find( "div[aria-expanded='true']" ).length, 1, "second submenu collapsed" ); + element.trigger( "mouseleave" ); + setTimeout( menumouseleave4, 350 ); + } + function menumouseleave4() { + equal( element.find( "div[aria-expanded='true']" ).length, 0, "first submenu collapsed" ); + start(); + } + + element.find( ":nth-child(7)" ).trigger( "mouseenter" ); + setTimeout( menumouseleave1, 350 ); +}); + + +asyncTest( "handle keyboard navigation on menu without scroll and without submenus", function() { + expect( 12 ); + var element = $( "#menu1" ).menu({ + select: function( event, ui ) { + log( $( ui.item[ 0 ] ).text() ); + }, + focus: function( event ) { + log( $( event.target ).find( ".ui-state-focus" ).parent().index() ); + } + }); + + log( "keydown", true ); + element[ 0 ].focus(); + setTimeout(function() { + element.simulate( "keydown", { keyCode: $.ui.keyCode.DOWN } ); + element.simulate( "keydown", { keyCode: $.ui.keyCode.DOWN } ); + equal( logOutput(), "keydown,0,1,2", "Keydown DOWN" ); + + log( "keydown", true ); + element.simulate( "keydown", { keyCode: $.ui.keyCode.UP } ); + equal( logOutput(), "keydown,1", "Keydown UP" ); + + log( "keydown", true ); + element.simulate( "keydown", { keyCode: $.ui.keyCode.LEFT } ); + equal( logOutput(), "keydown", "Keydown LEFT (no effect)" ); + + log( "keydown", true ); + element.simulate( "keydown", { keyCode: $.ui.keyCode.RIGHT } ); + equal( logOutput(), "keydown", "Keydown RIGHT (no effect)" ); + + log( "keydown", true ); + element.simulate( "keydown", { keyCode: $.ui.keyCode.PAGE_DOWN } ); + equal( logOutput(), "keydown,4", "Keydown PAGE_DOWN" ); + + log( "keydown", true ); + element.simulate( "keydown", { keyCode: $.ui.keyCode.PAGE_DOWN } ); + equal( logOutput(), "keydown", "Keydown PAGE_DOWN (no effect)" ); + + log( "keydown", true ); + element.simulate( "keydown", { keyCode: $.ui.keyCode.PAGE_UP } ); + equal( logOutput(), "keydown,0", "Keydown PAGE_UP" ); + + log( "keydown", true ); + element.simulate( "keydown", { keyCode: $.ui.keyCode.PAGE_UP } ); + equal( logOutput(), "keydown", "Keydown PAGE_UP (no effect)" ); + + log( "keydown", true ); + element.simulate( "keydown", { keyCode: $.ui.keyCode.END } ); + equal( logOutput(), "keydown,4", "Keydown END" ); + + log( "keydown", true ); + element.simulate( "keydown", { keyCode: $.ui.keyCode.HOME } ); + equal( logOutput(), "keydown,0", "Keydown HOME" ); + + log( "keydown", true ); + element.simulate( "keydown", { keyCode: $.ui.keyCode.ESCAPE } ); + equal( logOutput(), "keydown", "Keydown ESCAPE (no effect)" ); + + log( "keydown", true ); + element.simulate( "keydown", { keyCode: $.ui.keyCode.ENTER } ); + equal( logOutput(), "keydown,Aberdeen", "Keydown ENTER" ); + + start(); + }); +}); + +asyncTest( "handle keyboard navigation on menu without scroll and with submenus", function() { + expect( 16 ); + var element = $( "#menu2" ).menu({ + select: function( event, ui ) { + log( $( ui.item[0] ).text() ); + }, + focus: function( event ) { + log( $( event.target ).find( ".ui-state-focus" ).parent().index() ); + } + }); + + log( "keydown", true ); + element.one( "menufocus", function() { + element.simulate( "keydown", { keyCode: $.ui.keyCode.DOWN } ); + element.simulate( "keydown", { keyCode: $.ui.keyCode.DOWN } ); + equal( logOutput(), "keydown,1,2", "Keydown DOWN" ); + setTimeout( menukeyboard1, 50 ); + }); + element.focus(); + + function menukeyboard1() { + log( "keydown", true ); + element.simulate( "keydown", { keyCode: $.ui.keyCode.UP } ); + element.simulate( "keydown", { keyCode: $.ui.keyCode.UP } ); + equal( logOutput(), "keydown,1,0", "Keydown UP" ); + + log( "keydown", true ); + element.simulate( "keydown", { keyCode: $.ui.keyCode.LEFT } ); + equal( logOutput(), "keydown", "Keydown LEFT (no effect)" ); + + log( "keydown", true ); + element.simulate( "keydown", { keyCode: $.ui.keyCode.DOWN } ); + element.simulate( "keydown", { keyCode: $.ui.keyCode.DOWN } ); + element.simulate( "keydown", { keyCode: $.ui.keyCode.DOWN } ); + element.simulate( "keydown", { keyCode: $.ui.keyCode.DOWN } ); + element.simulate( "keydown", { keyCode: $.ui.keyCode.RIGHT } ); + + setTimeout(function() { + equal( logOutput(), "keydown,1,2,3,4,0", "Keydown RIGHT (open submenu)" ); + setTimeout( menukeyboard2, 50 ); + }, 50 ); + } + + function menukeyboard2() { + log( "keydown", true ); + element.simulate( "keydown", { keyCode: $.ui.keyCode.LEFT } ); + equal( logOutput(), "keydown,4", "Keydown LEFT (close submenu)" ); + + // re-open submenu + element.simulate( "keydown", { keyCode: $.ui.keyCode.RIGHT } ); + setTimeout( menukeyboard3, 50 ); + } + + function menukeyboard3() { + log( "keydown", true ); + element.simulate( "keydown", { keyCode: $.ui.keyCode.PAGE_DOWN } ); + equal( logOutput(), "keydown,2", "Keydown PAGE_DOWN" ); + + log( "keydown", true ); + element.simulate( "keydown", { keyCode: $.ui.keyCode.PAGE_DOWN } ); + equal( logOutput(), "keydown", "Keydown PAGE_DOWN (no effect)" ); + + log( "keydown", true ); + element.simulate( "keydown", { keyCode: $.ui.keyCode.PAGE_UP } ); + equal( logOutput(), "keydown,0", "Keydown PAGE_UP" ); + + log( "keydown", true ); + element.simulate( "keydown", { keyCode: $.ui.keyCode.PAGE_UP } ); + equal( logOutput(), "keydown", "Keydown PAGE_UP (no effect)" ); + + log( "keydown", true ); + element.simulate( "keydown", { keyCode: $.ui.keyCode.END } ); + equal( logOutput(), "keydown,2", "Keydown END" ); + + log( "keydown", true ); + element.simulate( "keydown", { keyCode: $.ui.keyCode.HOME } ); + equal( logOutput(), "keydown,0", "Keydown HOME" ); + + log( "keydown", true ); + element.simulate( "keydown", { keyCode: $.ui.keyCode.ESCAPE } ); + equal( logOutput(), "keydown,4", "Keydown ESCAPE (close submenu)" ); + + log( "keydown", true ); + element.simulate( "keydown", { keyCode: $.ui.keyCode.SPACE } ); + setTimeout( menukeyboard4, 50 ); + } + + function menukeyboard4() { + equal( logOutput(), "keydown,0", "Keydown SPACE (open submenu)" ); + + log( "keydown", true ); + element.simulate( "keydown", { keyCode: $.ui.keyCode.ESCAPE } ); + equal( logOutput(), "keydown,4", "Keydown ESCAPE (close submenu)" ); + + log( "keydown", true ); + element.simulate( "keydown", { keyCode: $.ui.keyCode.DOWN } ); + element.simulate( "keydown", { keyCode: $.ui.keyCode.DOWN } ); + element.simulate( "keydown", { keyCode: $.ui.keyCode.RIGHT } ); + setTimeout( function() { + element.simulate( "keydown", { keyCode: $.ui.keyCode.DOWN } ); + element.simulate( "keydown", { keyCode: $.ui.keyCode.RIGHT } ); + setTimeout( function() { + element.simulate( "keydown", { keyCode: $.ui.keyCode.DOWN } ); + element.simulate( "keydown", { keyCode: $.ui.keyCode.DOWN } ); + element.simulate( "keydown", { keyCode: $.ui.keyCode.DOWN } ); + equal( logOutput(), "keydown,5,6,0,1,0,2,4,0", "Keydown skip dividers and items without anchors" ); + + log( "keydown", true ); + element.simulate( "keydown", { keyCode: $.ui.keyCode.ENTER } ); + setTimeout( menukeyboard6, 50 ); + }, 50 ); + }, 50 ); + } + + function menukeyboard6() { + equal( logOutput(), "keydown,Ada", "Keydown ENTER (open submenu)" ); + start(); + } +}); + +asyncTest( "handle keyboard navigation on menu with scroll and without submenus", function() { + expect( 14 ); + var element = $( "#menu3" ).menu({ + select: function( event, ui ) { + log( $( ui.item[ 0 ] ).text() ); + }, + focus: function( event ) { + log( $( event.target ).find( ".ui-state-focus" ).parent().index()); + } + }); + + log( "keydown", true ); + element[ 0 ].focus(); + setTimeout(function() { + element.simulate( "keydown", { keyCode: $.ui.keyCode.DOWN } ); + element.simulate( "keydown", { keyCode: $.ui.keyCode.DOWN } ); + equal( logOutput(), "keydown,0,1,2", "Keydown DOWN" ); + + log( "keydown", true ); + element.simulate( "keydown", { keyCode: $.ui.keyCode.UP } ); + element.simulate( "keydown", { keyCode: $.ui.keyCode.UP } ); + equal( logOutput(), "keydown,1,0", "Keydown UP" ); + + log( "keydown", true ); + element.simulate( "keydown", { keyCode: $.ui.keyCode.LEFT } ); + equal( logOutput(), "keydown", "Keydown LEFT (no effect)" ); + + log( "keydown", true ); + element.simulate( "keydown", { keyCode: $.ui.keyCode.RIGHT } ); + equal( logOutput(), "keydown", "Keydown RIGHT (no effect)" ); + + log( "keydown", true ); + element.simulate( "keydown", { keyCode: $.ui.keyCode.PAGE_DOWN } ); + equal( logOutput(), "keydown,10", "Keydown PAGE_DOWN" ); + + log( "keydown", true ); + element.simulate( "keydown", { keyCode: $.ui.keyCode.PAGE_DOWN } ); + equal( logOutput(), "keydown,20", "Keydown PAGE_DOWN" ); + + log( "keydown", true ); + element.simulate( "keydown", { keyCode: $.ui.keyCode.PAGE_UP } ); + equal( logOutput(), "keydown,10", "Keydown PAGE_UP" ); + + log( "keydown", true ); + element.simulate( "keydown", { keyCode: $.ui.keyCode.PAGE_UP } ); + equal( logOutput(), "keydown,0", "Keydown PAGE_UP" ); + + log( "keydown", true ); + element.simulate( "keydown", { keyCode: $.ui.keyCode.PAGE_UP } ); + equal( logOutput(), "keydown", "Keydown PAGE_UP (no effect)" ); + + log( "keydown", true ); + element.simulate( "keydown", { keyCode: $.ui.keyCode.END } ); + equal( logOutput(), "keydown,37", "Keydown END" ); + + log( "keydown", true ); + element.simulate( "keydown", { keyCode: $.ui.keyCode.PAGE_DOWN } ); + equal( logOutput(), "keydown", "Keydown PAGE_DOWN (no effect)" ); + + log( "keydown", true ); + element.simulate( "keydown", { keyCode: $.ui.keyCode.HOME } ); + equal( logOutput(), "keydown,0", "Keydown HOME" ); + + log( "keydown", true ); + element.simulate( "keydown", { keyCode: $.ui.keyCode.ESCAPE } ); + equal( logOutput(), "keydown", "Keydown ESCAPE (no effect)" ); + + log( "keydown", true ); + element.simulate( "keydown", { keyCode: $.ui.keyCode.ENTER } ); + equal( logOutput(), "keydown,Aberdeen", "Keydown ENTER" ); + + start(); + }); +}); + +asyncTest( "handle keyboard navigation on menu with scroll and with submenus", function() { + expect( 14 ); + var element = $( "#menu4" ).menu({ + select: function( event, ui ) { + log( $( ui.item[ 0 ] ).text() ); + }, + focus: function( event ) { + log( $( event.target ).find( ".ui-state-focus" ).parent().index()); + } + }); + + log( "keydown", true ); + element.one( "menufocus", function() { + element.simulate( "keydown", { keyCode: $.ui.keyCode.DOWN } ); + element.simulate( "keydown", { keyCode: $.ui.keyCode.DOWN } ); + equal( logOutput(), "keydown,1,2", "Keydown DOWN" ); + setTimeout( menukeyboard1, 50 ); + }); + element.focus(); + + function menukeyboard1() { + log( "keydown", true ); + element.simulate( "keydown", { keyCode: $.ui.keyCode.UP } ); + element.simulate( "keydown", { keyCode: $.ui.keyCode.UP } ); + equal( logOutput(), "keydown,1,0", "Keydown UP" ); + + log( "keydown", true ); + element.simulate( "keydown", { keyCode: $.ui.keyCode.LEFT } ); + equal( logOutput(), "keydown", "Keydown LEFT (no effect)" ); + + log( "keydown", true ); + element.simulate( "keydown", { keyCode: $.ui.keyCode.DOWN } ); + element.simulate( "keydown", { keyCode: $.ui.keyCode.RIGHT } ); + + setTimeout( function() { + equal( logOutput(), "keydown,1,0", "Keydown RIGHT (open submenu)" ); + }, 50 ); + setTimeout( menukeyboard2, 50 ); + } + + function menukeyboard2() { + log( "keydown", true ); + element.simulate( "keydown", { keyCode: $.ui.keyCode.LEFT } ); + equal( logOutput(), "keydown,1", "Keydown LEFT (close submenu)" ); + + // re-open submenu + element.simulate( "keydown", { keyCode: $.ui.keyCode.RIGHT } ); + setTimeout( menukeyboard3, 50 ); + } + + function menukeyboard3() { + log( "keydown", true ); + element.simulate( "keydown", { keyCode: $.ui.keyCode.PAGE_DOWN } ); + equal( logOutput(), "keydown,10", "Keydown PAGE_DOWN" ); + + log( "keydown", true ); + element.simulate( "keydown", { keyCode: $.ui.keyCode.PAGE_DOWN } ); + equal( logOutput(), "keydown,20", "Keydown PAGE_DOWN" ); + + log( "keydown", true ); + element.simulate( "keydown", { keyCode: $.ui.keyCode.PAGE_UP } ); + equal( logOutput(), "keydown,10", "Keydown PAGE_UP" ); + + log( "keydown", true ); + element.simulate( "keydown", { keyCode: $.ui.keyCode.PAGE_UP } ); + equal( logOutput(), "keydown,0", "Keydown PAGE_UP" ); + + log( "keydown", true ); + element.simulate( "keydown", { keyCode: $.ui.keyCode.END } ); + equal( logOutput(), "keydown,27", "Keydown END" ); + + log( "keydown", true ); + element.simulate( "keydown", { keyCode: $.ui.keyCode.HOME } ); + equal( logOutput(), "keydown,0", "Keydown HOME" ); + + log( "keydown", true ); + element.simulate( "keydown", { keyCode: $.ui.keyCode.ESCAPE } ); + equal( logOutput(), "keydown,1", "Keydown ESCAPE (close submenu)" ); + + log( "keydown", true ); + element.simulate( "keydown", { keyCode: $.ui.keyCode.ENTER } ); + setTimeout( menukeyboard4, 50 ); + } + + function menukeyboard4() { + equal( logOutput(), "keydown,0", "Keydown ENTER (open submenu)" ); + + log( "keydown", true ); + element.simulate( "keydown", { keyCode: $.ui.keyCode.ENTER } ); + equal( logOutput(), "keydown,Aberdeen", "Keydown ENTER (select item)" ); + + start(); + } +}); + +asyncTest( "handle keyboard navigation and mouse click on menu with disabled items", function() { + expect( 6 ); + var element = $( "#menu6" ).menu({ + select: function( event, ui ) { + log( $( ui.item[0] ).text() ); + }, + focus: function( event ) { + log( $( event.target ).find( ".ui-state-focus" ).parent().index()); + } + }); + + log( "keydown", true ); + element.one( "menufocus", function() { + element.simulate( "keydown", { keyCode: $.ui.keyCode.DOWN } ); + element.simulate( "keydown", { keyCode: $.ui.keyCode.ENTER } ); + equal( logOutput(), "keydown,1", "Keydown focus but not select disabled item" ); + setTimeout( menukeyboard1, 50 ); + }); + element.focus(); + + function menukeyboard1() { + log( "keydown", true ); + element.simulate( "keydown", { keyCode: $.ui.keyCode.DOWN } ); + element.simulate( "keydown", { keyCode: $.ui.keyCode.DOWN } ); + element.simulate( "keydown", { keyCode: $.ui.keyCode.DOWN } ); + equal( logOutput(), "keydown,2,3,4", "Keydown focus disabled item with submenu" ); + + log( "keydown", true ); + element.simulate( "keydown", { keyCode: $.ui.keyCode.LEFT } ); + equal( logOutput(), "keydown", "Keydown LEFT (no effect)" ); + + log( "keydown", true ); + element.simulate( "keydown", { keyCode: $.ui.keyCode.RIGHT } ); + + setTimeout( function() { + equal( logOutput(), "keydown", "Keydown RIGHT (no effect on disabled sub-menu)" ); + + log( "keydown", true ); + element.simulate( "keydown", { keyCode: $.ui.keyCode.ENTER } ); + + setTimeout( function() { + equal( logOutput(), "keydown", "Keydown ENTER (no effect on disabled sub-menu)" ); + log( "click", true ); + click( element, "1" ); + equal( logOutput(), "click", "Click disabled item (no effect)" ); + start(); + }, 50 ); + }, 50 ); + } +}); + +asyncTest( "handle keyboard navigation with spelling of menu items", function() { + expect( 2 ); + var element = $( "#menu2" ).menu({ + focus: function( event ) { + log( $( event.target ).find( ".ui-state-focus" ).parent().index() ); + } + }); + + log( "keydown", true ); + element.one( "menufocus", function() { + element.simulate( "keydown", { keyCode: 65 } ); + element.simulate( "keydown", { keyCode: 68 } ); + element.simulate( "keydown", { keyCode: 68 } ); + equal( logOutput(), "keydown,0,1,3", "Keydown focus Addyston by spelling the first 3 letters" ); + element.simulate( "keydown", { keyCode: 68 } ); + equal( logOutput(), "keydown,0,1,3,4", "Keydown focus Delphi by repeating the 'd' again" ); + start(); + }); + element[ 0 ].focus(); +}); + +test( "ensure default is prevented when clicking on anchors in disabled menus ", function() { + expect( 1 ); + var element = $( "#menu1" ).menu(); + element.bind( "click.menu", function(event) { + if ( !event.isDefaultPrevented() ) { + log(); + } + }); + log( "click", true ); + click( element, "1" ); + log( "afterclick,disable" ); + element.menu( "option", "disabled", true ); + click( element, "2" ); + click( element, "3" ); + click( element, "1" ); + log( "enable" ); + element.menu( "option", "disabled", false ); + click( element, "3" ); + equal( logOutput(), "click,1,afterclick,disable,enable,3", "Click order not valid." ); +}); + +test( "#9469: Stopping propagation in a select event should not suppress subsequent select events.", function() { + expect( 1 ); + var element = $( "#menu1" ).menu({ + select: function( event ) { + log(); + event.stopPropagation(); + } + }); + + click( element, "1" ); + click( element, "2" ); + + equal( logOutput(), "1,2", "Both select events were not triggered." ); +}); + +})( jQuery ); diff --git a/apps/it/static/js/ui/tests/unit/menu/menu_methods.js b/apps/it/static/js/ui/tests/unit/menu/menu_methods.js new file mode 100644 index 0000000..51d7729 --- /dev/null +++ b/apps/it/static/js/ui/tests/unit/menu/menu_methods.js @@ -0,0 +1,117 @@ +(function( $ ) { + +var log = TestHelpers.menu.log, + logOutput = TestHelpers.menu.logOutput, + click = TestHelpers.menu.click; + +module( "menu: methods", { + setup: function() { + TestHelpers.menu.clearLog(); + } +}); + +test( "destroy", function() { + expect( 4 ); + domEqual( "#menu1", function() { + $( "#menu1" ).menu().menu( "destroy" ); + }); + domEqual( "#menu2", function() { + $( "#menu2" ).menu().menu( "destroy" ); + }); + domEqual( "#menu5", function() { + $( "#menu5").menu().menu( "destroy" ); + }); + domEqual( "#menu6", function() { + $( "#menu6" ).menu().menu( "destroy" ); + }); +}); + +test( "enable/disable", function() { + expect( 3 ); + var element = $( "#menu1" ).menu({ + select: function() { + log(); + } + }); + element.menu( "disable" ); + ok( element.is( ".ui-state-disabled" ), "Missing ui-state-disabled class" ); + log( "click", true ); + click( element, "1" ); + log( "afterclick" ); + element.menu( "enable" ); + ok( element.not( ".ui-state-disabled" ), "Has ui-state-disabled class" ); + log( "click" ); + click( element, "1" ); + log( "afterclick" ); + equal( logOutput(), "click,afterclick,click,1,afterclick", "Click order not valid." ); +}); + +test( "refresh", function() { + expect( 5 ); + var element = $( "#menu1" ).menu(); + equal( element.find( ".ui-menu-item" ).length, 5, "Incorrect number of menu items" ); + element.append( "<li><a href='#'>test item</a></li>" ).menu( "refresh" ); + equal( element.find( ".ui-menu-item" ).length, 6, "Incorrect number of menu items" ); + element.find( ".ui-menu-item:last" ).remove().end().menu( "refresh" ); + equal( element.find( ".ui-menu-item" ).length, 5, "Incorrect number of menu items" ); + element.append( "<li>---</li>" ).menu( "refresh" ); + equal( element.find( ".ui-menu-item" ).length, 5, "Incorrect number of menu items" ); + element.children( ":last" ).remove().end().menu( "refresh" ); + equal( element.find( ".ui-menu-item" ).length, 5, "Incorrect number of menu items" ); +}); + +test( "refresh submenu", function() { + expect( 2 ); + var element = $( "#menu2" ).menu(); + equal( element.find( "ul:first .ui-menu-item" ).length, 3 ); + element.find( "ul" ).addBack().append( "<li><a href=\"#\">New Item</a></li>" ); + element.menu( "refresh" ); + equal( element.find( "ul:first .ui-menu-item" ).length, 4 ); +}); + +test( "refresh icons (see #9377)", function() { + expect( 3 ); + var element = $( "#menu1" ).menu(); + ok( !element.hasClass( "ui-menu-icons") ); + element.find( "li:first a" ).html( "<span class='ui-icon ui-icon-disk'></span>Save</a>" ); + element.menu( "refresh" ); + + ok( element.hasClass( "ui-menu-icons" ) ); + element.find( "li:first a" ).html( "Save" ); + element.menu( "refresh" ); + ok( !element.hasClass( "ui-menu-icons" ) ); +}); + +test( "widget", function() { + expect( 2 ); + var element = $( "#menu1" ).menu(), + widgetElement = element.menu( "widget" ); + equal( widgetElement.length, 1, "one element" ); + strictEqual( widgetElement[ 0 ], element[ 0 ], "same element" ); +}); + +// TODO: test focus method + +// TODO: test blur method + +// TODO: test collapseAll method + +// TODO: test collapse method + +// TODO: test expand method + +// TODO: test next method + +// TODO: test prev method + +// TODO: test isFirstItem method + +// TODO: test isLastItem method + +// TODO: test nextPage method + +// TODO: test prevPage method + +// TODO: test select method + +})( jQuery ); diff --git a/apps/it/static/js/ui/tests/unit/menu/menu_options.js b/apps/it/static/js/ui/tests/unit/menu/menu_options.js new file mode 100644 index 0000000..27cea67 --- /dev/null +++ b/apps/it/static/js/ui/tests/unit/menu/menu_options.js @@ -0,0 +1,115 @@ +(function( $ ) { + +var log = TestHelpers.menu.log, + logOutput = TestHelpers.menu.logOutput, + click = TestHelpers.menu.click; + +module( "menu: options", { + setup: function() { + TestHelpers.menu.clearLog(); + } +}); + +test( "{ disabled: true }", function() { + expect( 2 ); + var element = $( "#menu1" ).menu({ + disabled: true, + select: function() { + log(); + } + }); + ok( element.hasClass( "ui-state-disabled" ), "Missing ui-state-disabled class" ); + log( "click", true ); + click( element, "1" ); + log( "afterclick" ); + equal( logOutput(), "click,afterclick", "Click order not valid." ); +}); + +test( "{ disabled: false }", function() { + expect( 2 ); + var element = $( "#menu1" ).menu({ + disabled: false, + select: function() { + log(); + } + }); + ok( !element.hasClass( "ui-state-disabled" ), "Has ui-state-disabled class" ); + log( "click", true ); + click( element, "1" ); + log( "afterclick" ); + equal( logOutput(), "click,1,afterclick", "Click order not valid." ); +}); + +test( "{ icons: default }", function() { + expect( 2 ); + var element = $( "#menu2" ).menu(); + equal( element.find( ".ui-menu-icon" ).attr( "class" ), "ui-menu-icon ui-icon ui-icon-carat-1-e" ); + + element.menu( "option", "icons.submenu", "ui-icon-triangle-1-e" ); + equal( element.find( ".ui-menu-icon" ).attr( "class" ), "ui-menu-icon ui-icon ui-icon-triangle-1-e" ); +}); + +test( "{ icons: { submenu: 'custom' } }", function() { + expect( 1 ); + var element = $( "#menu2" ).menu({ + icons: { + submenu: "custom-class" + } + }); + equal( element.find( ".ui-menu-icon" ).attr( "class" ), "ui-menu-icon ui-icon custom-class" ); +}); + +// TODO: test menus option + +// TODO: test position option + +test( "{ role: 'menu' } ", function() { + var element = $( "#menu1" ).menu(), + items = element.find( "li" ); + expect( 2 + 5 * items.length ); + equal( element.attr( "role" ), "menu" ); + ok( items.length > 0, "number of menu items" ); + items.each(function( item ) { + ok( $( this ).hasClass( "ui-menu-item" ), "menu item ("+ item + ") class for item" ); + equal( $( this ).attr( "role" ), "presentation", "menu item ("+ item + ") role" ); + equal( $( "a", this ).attr( "role" ), "menuitem", "menu item ("+ item + ") role" ); + ok( $( "a", this ).hasClass( "ui-corner-all" ), "a element class for menu item ("+ item + ")" ); + equal( $( "a", this ).attr( "tabindex" ), "-1", "a element tabindex for menu item ("+ item + ")" ); + }); +}); + +test( "{ role: 'listbox' } ", function() { + var element = $( "#menu1" ).menu({ + role: "listbox" + }), + items = element.find( "li" ); + expect( 2 + 5 * items.length ); + equal( element.attr( "role" ), "listbox" ); + ok( items.length > 0, "number of menu items" ); + items.each(function( item ) { + ok( $( this ).hasClass( "ui-menu-item" ), "menu item ("+ item + ") class for item" ); + equal( $( this ).attr( "role" ), "presentation", "menu item ("+ item + ") role" ); + equal( $( "a", this ).attr( "role" ), "option", "menu item ("+ item + ") role" ); + ok( $( "a", this ).hasClass( "ui-corner-all" ), "a element class for menu item ("+ item + ")" ); + equal( $( "a", this ).attr( "tabindex" ), "-1", "a element tabindex for menu item ("+ item + ")" ); + }); +}); + +test( "{ role: null }", function() { + var element = $( "#menu1" ).menu({ + role: null + }), + items = element.find( "li" ); + expect( 2 + 5 * items.length ); + strictEqual( element.attr( "role" ), undefined ); + ok( items.length > 0, "number of menu items" ); + items.each(function( item ) { + ok( $( this ).hasClass( "ui-menu-item" ), "menu item ("+ item + ") class for item" ); + equal( $( this ).attr( "role" ), "presentation", "menu item ("+ item + ") role" ); + equal( $( "a", this ).attr( "role" ), undefined, "menu item ("+ item + ") role" ); + ok( $( "a", this ).hasClass( "ui-corner-all" ), "a element class for menu item ("+ item + ")" ); + equal( $( "a", this ).attr( "tabindex" ), "-1", "a element tabindex for menu item ("+ item + ")" ); + }); +}); + +})( jQuery ); diff --git a/apps/it/static/js/ui/tests/unit/menu/menu_test_helpers.js b/apps/it/static/js/ui/tests/unit/menu/menu_test_helpers.js new file mode 100644 index 0000000..b6c09ad --- /dev/null +++ b/apps/it/static/js/ui/tests/unit/menu/menu_test_helpers.js @@ -0,0 +1,31 @@ +(function() { + +var lastItem, + log = []; + +TestHelpers.menu = { + log: function( message, clear ) { + if ( clear ) { + log.length = 0; + } + if ( message === undefined ) { + message = lastItem; + } + log.push( $.trim( message ) ); + }, + + logOutput: function() { + return log.join( "," ); + }, + + clearLog: function() { + log.length = 0; + }, + + click: function( menu, item ) { + lastItem = item; + menu.children( ":eq(" + item + ")" ).find( "a:first" ).trigger( "click" ); + } +}; + +})(); diff --git a/apps/it/static/js/ui/tests/unit/position/all.html b/apps/it/static/js/ui/tests/unit/position/all.html new file mode 100644 index 0000000..d866c6a --- /dev/null +++ b/apps/it/static/js/ui/tests/unit/position/all.html @@ -0,0 +1,30 @@ +<!doctype html> +<html lang="en"> +<head> + <meta charset="utf-8"> + <title>jQuery UI Position Test Suite</title> + + <script src="../../../jquery-1.10.2.js"></script> + + <link rel="stylesheet" href="../../../external/qunit.css"> + <link rel="stylesheet" href="../qunit-composite.css"> + <script src="../../../external/qunit.js"></script> + <script src="../qunit-composite.js"></script> + <script src="../subsuite.js"></script> + + <script> + testAllVersions( "position" ); + </script> +</head> +<body> + +<h1 id="qunit-header">jQuery UI Position Test Suite</h1> +<h2 id="qunit-banner"></h2> +<div id="qunit-testrunner-toolbar"></div> +<h2 id="qunit-userAgent"></h2> +<ol id="qunit-tests"></ol> +<div id="qunit-fixture"> + +</div> +</body> +</html> diff --git a/apps/it/static/js/ui/tests/unit/position/position.html b/apps/it/static/js/ui/tests/unit/position/position.html new file mode 100644 index 0000000..f3b1ad8 --- /dev/null +++ b/apps/it/static/js/ui/tests/unit/position/position.html @@ -0,0 +1,53 @@ +<!doctype html> +<html lang="en"> +<head> + <meta charset="utf-8"> + <title>jQuery UI Position Test Suite</title> + + <script src="../../jquery.js"></script> + <link rel="stylesheet" href="../../../external/qunit.css"> + <script src="../../../external/qunit.js"></script> + <script src="../../jquery.simulate.js"></script> + <script src="../testsuite.js"></script> + <script> + TestHelpers.loadResources({ + js: [ "ui/jquery.ui.position.js" ] + }); + </script> + + <script src="position_core.js"></script> + + <script src="../swarminject.js"></script> +</head> +<body> +<div id="qunit" style="position:relative; z-index:2;"></div> + +<!-- +elements smaller than 20px have a line-height set on them to avoid a bug in IE6 +.height() returns the greater of the height and line-height +--> + +<div id="qunit-fixture" style="top: 0; left: 0; z-index:1"> + <div id="el1" style="position: absolute; width: 6px; height: 6px; line-height: 6px;"></div> + <div id="el2" style="position: absolute; width: 6px; height: 6px; line-height: 6px;"></div> + <div id="parent" style="position: absolute; width: 6px; height: 6px; top: 4px; left: 4px; line-height: 6px;"></div> + <div id="within" style="position: absolute; width: 12px; height: 12px; top: 2px; left: 0px; line-height: 12px;"></div> + + <div id="scrollx" style="position: absolute; top: 0px; left: 0px"> + <div id="elx" style="position: absolute; width: 10px; height: 10px; line-height: 10px;"></div> + <div id="parentx" style="position: absolute; width: 20px; height: 20px; top: 40px; left: 40px;"></div> + </div> + + <div style="position: absolute; height: 5000px; width: 5000px;"></div> + + <div id="fractions-parent" style="position: absolute; left: 10.7432222px; top: 10.532325px; height: 30px; width: 201px;"> + <div id="fractions-element"></div> + </div> + + <div id="bug-5280" style="height: 30px; width: 201px;"> + <div style="width: 50px; height: 10px;"></div> + </div> +</div> + +</body> +</html> diff --git a/apps/it/static/js/ui/tests/unit/position/position_core.js b/apps/it/static/js/ui/tests/unit/position/position_core.js new file mode 100644 index 0000000..5b1872a --- /dev/null +++ b/apps/it/static/js/ui/tests/unit/position/position_core.js @@ -0,0 +1,729 @@ +(function( $ ) { + +var win = $( window ), + scrollTopSupport = function() { + var support = win.scrollTop( 1 ).scrollTop() === 1; + win.scrollTop( 0 ); + scrollTopSupport = function() { + return support; + }; + return support; + }; + +module( "position", { + setup: function() { + win.scrollTop( 0 ).scrollLeft( 0 ); + } +}); + +TestHelpers.testJshint( "position" ); + +test( "my, at, of", function() { + expect( 4 ); + + $( "#elx" ).position({ + my: "left top", + at: "left top", + of: "#parentx", + collision: "none" + }); + deepEqual( $( "#elx" ).offset(), { top: 40, left: 40 }, "left top, left top" ); + + $( "#elx" ).position({ + my: "left top", + at: "left bottom", + of: "#parentx", + collision: "none" + }); + deepEqual( $( "#elx" ).offset(), { top: 60, left: 40 }, "left top, left bottom" ); + + $( "#elx" ).position({ + my: "left", + at: "bottom", + of: "#parentx", + collision: "none" + }); + deepEqual( $( "#elx" ).offset(), { top: 55, left: 50 }, "left, bottom" ); + + $( "#elx" ).position({ + my: "left foo", + at: "bar baz", + of: "#parentx", + collision: "none" + }); + deepEqual( $( "#elx" ).offset(), { top: 45, left: 50 }, "left foo, bar baz" ); +}); + +test( "multiple elements", function() { + expect( 3 ); + + var elements = $( "#el1, #el2" ), + result = elements.position({ + my: "left top", + at: "left bottom", + of: "#parent", + collision: "none" + }), + expected = { top: 10, left: 4 }; + + deepEqual( result, elements ); + elements.each(function() { + deepEqual( $( this ).offset(), expected ); + }); +}); + +test( "positions", function() { + expect( 18 ); + + var offsets = { + left: 0, + center: 3, + right: 6, + top: 0, + bottom: 6 + }, + start = { left: 4, top: 4 }, + el = $( "#el1" ); + + $.each( [ 0, 1 ], function( my ) { + $.each( [ "top", "center", "bottom" ], function( vindex, vertical ) { + $.each( [ "left", "center", "right" ], function( hindex, horizontal ) { + var _my = my ? horizontal + " " + vertical : "left top", + _at = !my ? horizontal + " " + vertical : "left top"; + el.position({ + my: _my, + at: _at, + of: "#parent", + collision: "none" + }); + deepEqual( el.offset(), { + top: start.top + offsets[ vertical ] * (my ? -1 : 1), + left: start.left + offsets[ horizontal ] * (my ? -1 : 1) + }, "Position via " + QUnit.jsDump.parse({ my: _my, at: _at }) ); + }); + }); + }); +}); + +test( "of", function() { + expect( 9 + (scrollTopSupport() ? 1 : 0) ); + + var event; + + $( "#elx" ).position({ + my: "left top", + at: "left top", + of: "#parentx", + collision: "none" + }); + deepEqual( $( "#elx" ).offset(), { top: 40, left: 40 }, "selector" ); + + $( "#elx" ).position({ + my: "left top", + at: "left bottom", + of: $( "#parentx"), + collision: "none" + }); + deepEqual( $( "#elx" ).offset(), { top: 60, left: 40 }, "jQuery object" ); + + $( "#elx" ).position({ + my: "left top", + at: "left top", + of: $( "#parentx" )[ 0 ], + collision: "none" + }); + deepEqual( $( "#elx" ).offset(), { top: 40, left: 40 }, "DOM element" ); + + $( "#elx" ).position({ + my: "right bottom", + at: "right bottom", + of: document, + collision: "none" + }); + deepEqual( $( "#elx" ).offset(), { + top: $( document ).height() - 10, + left: $( document ).width() - 10 + }, "document" ); + + $( "#elx" ).position({ + my: "right bottom", + at: "right bottom", + of: $( document ), + collision: "none" + }); + deepEqual( $( "#elx" ).offset(), { + top: $( document ).height() - 10, + left: $( document ).width() - 10 + }, "document as jQuery object" ); + + win.scrollTop( 0 ); + + $( "#elx" ).position({ + my: "right bottom", + at: "right bottom", + of: window, + collision: "none" + }); + deepEqual( $( "#elx" ).offset(), { + top: win.height() - 10, + left: win.width() - 10 + }, "window" ); + + $( "#elx" ).position({ + my: "right bottom", + at: "right bottom", + of: win, + collision: "none" + }); + deepEqual( $( "#elx" ).offset(), { + top: win.height() - 10, + left: win.width() - 10 + }, "window as jQuery object" ); + + if ( scrollTopSupport() ) { + win.scrollTop( 500 ).scrollLeft( 200 ); + $( "#elx" ).position({ + my: "right bottom", + at: "right bottom", + of: window, + collision: "none" + }); + deepEqual( $( "#elx" ).offset(), { + top: win.height() + 500 - 10, + left: win.width() + 200 - 10 + }, "window, scrolled" ); + win.scrollTop( 0 ).scrollLeft( 0 ); + } + + event = $.extend( $.Event( "someEvent" ), { pageX: 200, pageY: 300 } ); + $( "#elx" ).position({ + my: "left top", + at: "left top", + of: event, + collision: "none" + }); + deepEqual( $( "#elx" ).offset(), { + top: 300, + left: 200 + }, "event - left top, left top" ); + + event = $.extend( $.Event( "someEvent" ), { pageX: 400, pageY: 600 } ); + $( "#elx" ).position({ + my: "left top", + at: "right bottom", + of: event, + collision: "none" + }); + deepEqual( $( "#elx" ).offset(), { + top: 600, + left: 400 + }, "event - left top, right bottom" ); +}); + +test( "offsets", function() { + expect( 9 ); + + var offset; + + $( "#elx" ).position({ + my: "left top", + at: "left+10 bottom+10", + of: "#parentx", + collision: "none" + }); + deepEqual( $( "#elx" ).offset(), { top: 70, left: 50 }, "offsets in at" ); + + $( "#elx" ).position({ + my: "left+10 top-10", + at: "left bottom", + of: "#parentx", + collision: "none" + }); + deepEqual( $( "#elx" ).offset(), { top: 50, left: 50 }, "offsets in my" ); + + $( "#elx" ).position({ + my: "left top", + at: "left+50% bottom-10%", + of: "#parentx", + collision: "none" + }); + deepEqual( $( "#elx" ).offset(), { top: 58, left: 50 }, "percentage offsets in at" ); + + $( "#elx" ).position({ + my: "left-30% top+50%", + at: "left bottom", + of: "#parentx", + collision: "none" + }); + deepEqual( $( "#elx" ).offset(), { top: 65, left: 37 }, "percentage offsets in my" ); + + $( "#elx" ).position({ + my: "left-30.001% top+50.0%", + at: "left bottom", + of: "#parentx", + collision: "none" + }); + offset = $( "#elx" ).offset(); + equal( Math.round( offset.top ), 65, "decimal percentage offsets in my" ); + equal( Math.round( offset.left ), 37, "decimal percentage offsets in my" ); + + $( "#elx" ).position({ + my: "left+10.4 top-10.6", + at: "left bottom", + of: "#parentx", + collision: "none" + }); + offset = $( "#elx" ).offset(); + equal( Math.round( offset.top ), 49, "decimal offsets in my" ); + equal( Math.round( offset.left ), 50, "decimal offsets in my" ); + + $( "#elx" ).position({ + my: "left+right top-left", + at: "left-top bottom-bottom", + of: "#parentx", + collision: "none" + }); + deepEqual( $( "#elx" ).offset(), { top: 60, left: 40 }, "invalid offsets" ); +}); + +test( "using", function() { + expect( 10 ); + + var count = 0, + elems = $( "#el1, #el2" ), + of = $( "#parentx" ), + expectedPosition = { top: 60, left: 60 }, + expectedFeedback = { + target: { + element: of, + width: 20, + height: 20, + left: 40, + top: 40 + }, + element: { + width: 6, + height: 6, + left: 60, + top: 60 + }, + horizontal: "left", + vertical: "top", + important: "vertical" + }, + originalPosition = elems.position({ + my: "right bottom", + at: "rigt bottom", + of: "#parentx", + collision: "none" + }).offset(); + + elems.position({ + my: "left top", + at: "center+10 bottom", + of: "#parentx", + using: function( position, feedback ) { + deepEqual( this, elems[ count ], "correct context for call #" + count ); + deepEqual( position, expectedPosition, "correct position for call #" + count ); + deepEqual( feedback.element.element[ 0 ], elems[ count ] ); + delete feedback.element.element; + deepEqual( feedback, expectedFeedback ); + count++; + } + }); + + elems.each(function() { + deepEqual( $( this ).offset(), originalPosition, "elements not moved" ); + }); +}); + +function collisionTest( config, result, msg ) { + var elem = $( "#elx" ).position( $.extend({ + my: "left top", + at: "right bottom", + of: "#parent" + }, config ) ); + deepEqual( elem.offset(), result, msg ); +} + +function collisionTest2( config, result, msg ) { + collisionTest( $.extend({ + my: "right bottom", + at: "left top" + }, config ), result, msg ); +} + +test( "collision: fit, no collision", function() { + expect( 2 ); + + collisionTest({ + collision: "fit" + }, { + top: 10, + left: 10 + }, "no offset" ); + + collisionTest({ + collision: "fit", + at: "right+2 bottom+3" + }, { + top: 13, + left: 12 + }, "with offset" ); +}); + +// Currently failing in IE8 due to the iframe used by TestSwarm +if ( !/msie [\w.]+/.exec( navigator.userAgent.toLowerCase() ) ) { +test( "collision: fit, collision", function() { + expect( 2 + (scrollTopSupport() ? 1 : 0) ); + + collisionTest2({ + collision: "fit" + }, { + top: 0, + left: 0 + }, "no offset" ); + + collisionTest2({ + collision: "fit", + at: "left+2 top+3" + }, { + top: 0, + left: 0 + }, "with offset" ); + + if ( scrollTopSupport() ) { + win.scrollTop( 300 ).scrollLeft( 200 ); + collisionTest({ + collision: "fit" + }, { + top: 300, + left: 200 + }, "window scrolled" ); + + win.scrollTop( 0 ).scrollLeft( 0 ); + } +}); +} + +test( "collision: flip, no collision", function() { + expect( 2 ); + + collisionTest({ + collision: "flip" + }, { + top: 10, + left: 10 + }, "no offset" ); + + collisionTest({ + collision: "flip", + at: "right+2 bottom+3" + }, { + top: 13, + left: 12 + }, "with offset" ); +}); + +test( "collision: flip, collision", function() { + expect( 2 ); + + collisionTest2({ + collision: "flip" + }, { + top: 10, + left: 10 + }, "no offset" ); + + collisionTest2({ + collision: "flip", + at: "left+2 top+3" + }, { + top: 7, + left: 8 + }, "with offset" ); +}); + +test( "collision: flipfit, no collision", function() { + expect( 2 ); + + collisionTest({ + collision: "flipfit" + }, { + top: 10, + left: 10 + }, "no offset" ); + + collisionTest({ + collision: "flipfit", + at: "right+2 bottom+3" + }, { + top: 13, + left: 12 + }, "with offset" ); +}); + +test( "collision: flipfit, collision", function() { + expect( 2 ); + + collisionTest2({ + collision: "flipfit" + }, { + top: 10, + left: 10 + }, "no offset" ); + + collisionTest2({ + collision: "flipfit", + at: "left+2 top+3" + }, { + top: 7, + left: 8 + }, "with offset" ); +}); + +test( "collision: none, no collision", function() { + expect( 2 ); + + collisionTest({ + collision: "none" + }, { + top: 10, + left: 10 + }, "no offset" ); + + collisionTest({ + collision: "none", + at: "right+2 bottom+3" + }, { + top: 13, + left: 12 + }, "with offset" ); +}); + +test( "collision: none, collision", function() { + expect( 2 ); + + collisionTest2({ + collision: "none" + }, { + top: -6, + left: -6 + }, "no offset" ); + + collisionTest2({ + collision: "none", + at: "left+2 top+3" + }, { + top: -3, + left: -4 + }, "with offset" ); +}); + +test( "collision: fit, with margin", function() { + expect( 2 ); + + $( "#elx" ).css({ + marginTop: 6, + marginLeft: 4 + }); + + collisionTest({ + collision: "fit" + }, { + top: 10, + left: 10 + }, "right bottom" ); + + collisionTest2({ + collision: "fit" + }, { + top: 6, + left: 4 + }, "left top" ); +}); + +test( "collision: flip, with margin", function() { + expect( 3 ); + + $( "#elx" ).css({ + marginTop: 6, + marginLeft: 4 + }); + + collisionTest({ + collision: "flip" + }, { + top: 10, + left: 10 + }, "left top" ); + + collisionTest2({ + collision: "flip" + }, { + top: 10, + left: 10 + }, "right bottom" ); + + collisionTest2({ + collision: "flip", + my: "left top" + }, { + top: 0, + left: 4 + }, "right bottom" ); +}); + +test( "within", function() { + expect( 7 ); + + collisionTest({ + within: document + }, { + top: 10, + left: 10 + }, "within document" ); + + collisionTest({ + within: "#within", + collision: "fit" + }, { + top: 4, + left: 2 + }, "fit - right bottom" ); + + collisionTest2({ + within: "#within", + collision: "fit" + }, { + top: 2, + left: 0 + }, "fit - left top" ); + + collisionTest({ + within: "#within", + collision: "flip" + }, { + top: 10, + left: -6 + }, "flip - right bottom" ); + + collisionTest2({ + within: "#within", + collision: "flip" + }, { + top: 10, + left: -6 + }, "flip - left top" ); + + collisionTest({ + within: "#within", + collision: "flipfit" + }, { + top: 4, + left: 0 + }, "flipfit - right bottom" ); + + collisionTest2({ + within: "#within", + collision: "flipfit" + }, { + top: 4, + left: 0 + }, "flipfit - left top" ); +}); + +test( "with scrollbars", function() { + expect( 4 ); + + $( "#scrollx" ).css({ + width: 100, + height: 100, + left: 0, + top: 0 + }); + + collisionTest({ + of: "#scrollx", + collision: "fit", + within: "#scrollx" + }, { + top: 90, + left: 90 + }, "visible" ); + + $( "#scrollx" ).css({ + overflow: "scroll" + }); + + var scrollbarInfo = $.position.getScrollInfo( $.position.getWithinInfo( $( "#scrollx" ) ) ); + + collisionTest({ + of: "#scrollx", + collision: "fit", + within: "#scrollx" + }, { + top: 90 - scrollbarInfo.height, + left: 90 - scrollbarInfo.width + }, "scroll" ); + + $( "#scrollx" ).css({ + overflow: "auto" + }); + + collisionTest({ + of: "#scrollx", + collision: "fit", + within: "#scrollx" + }, { + top: 90, + left: 90 + }, "auto, no scroll" ); + + $( "#scrollx" ).css({ + overflow: "auto" + }).append( $("<div>").height(300).width(300) ); + + collisionTest({ + of: "#scrollx", + collision: "fit", + within: "#scrollx" + }, { + top: 90 - scrollbarInfo.height, + left: 90 - scrollbarInfo.width + }, "auto, with scroll" ); +}); + +test( "fractions", function() { + expect( 1 ); + + $( "#fractions-element" ).position({ + my: "left top", + at: "left top", + of: "#fractions-parent", + collision: "none" + }); + deepEqual( $( "#fractions-element" ).offset(), $( "#fractions-parent" ).offset(), "left top, left top" ); +}); + +test( "bug #5280: consistent results (avoid fractional values)", function() { + expect( 1 ); + + var wrapper = $( "#bug-5280" ), + elem = wrapper.children(), + offset1 = elem.position({ + my: "center", + at: "center", + of: wrapper, + collision: "none" + }).offset(), + offset2 = elem.position({ + my: "center", + at: "center", + of: wrapper, + collision: "none" + }).offset(); + deepEqual( offset1, offset2 ); +}); + +}( jQuery ) ); diff --git a/apps/it/static/js/ui/tests/unit/progressbar/all.html b/apps/it/static/js/ui/tests/unit/progressbar/all.html new file mode 100644 index 0000000..ed17c36 --- /dev/null +++ b/apps/it/static/js/ui/tests/unit/progressbar/all.html @@ -0,0 +1,30 @@ +<!doctype html> +<html lang="en"> +<head> + <meta charset="utf-8"> + <title>jQuery UI Progressbar Test Suite</title> + + <script src="../../../jquery-1.10.2.js"></script> + + <link rel="stylesheet" href="../../../external/qunit.css"> + <link rel="stylesheet" href="../qunit-composite.css"> + <script src="../../../external/qunit.js"></script> + <script src="../qunit-composite.js"></script> + <script src="../subsuite.js"></script> + + <script> + testAllVersions( "progressbar" ); + </script> +</head> +<body> + +<h1 id="qunit-header">jQuery UI Progressbar Test Suite</h1> +<h2 id="qunit-banner"></h2> +<div id="qunit-testrunner-toolbar"></div> +<h2 id="qunit-userAgent"></h2> +<ol id="qunit-tests"></ol> +<div id="qunit-fixture"> + +</div> +</body> +</html> diff --git a/apps/it/static/js/ui/tests/unit/progressbar/progressbar.html b/apps/it/static/js/ui/tests/unit/progressbar/progressbar.html new file mode 100644 index 0000000..70a4abe --- /dev/null +++ b/apps/it/static/js/ui/tests/unit/progressbar/progressbar.html @@ -0,0 +1,44 @@ +<!doctype html> +<html lang="en"> +<head> + <meta charset="utf-8"> + <title>jQuery UI Progressbar Test Suite</title> + + <script src="../../jquery.js"></script> + <link rel="stylesheet" href="../../../external/qunit.css"> + <script src="../../../external/qunit.js"></script> + <script src="../../jquery.simulate.js"></script> + <script src="../testsuite.js"></script> + <script> + TestHelpers.loadResources({ + css: [ "ui.core", "ui.progressbar" ], + js: [ + "ui/jquery.ui.core.js", + "ui/jquery.ui.widget.js", + "ui/jquery.ui.progressbar.js" + ] + }); + </script> + + <script src="progressbar_common.js"></script> + <script src="progressbar_core.js"></script> + <script src="progressbar_events.js"></script> + <script src="progressbar_methods.js"></script> + <script src="progressbar_options.js"></script> + + <script src="../swarminject.js"></script> +</head> +<body> + +<h1 id="qunit-header">jQuery UI Progressbar Test Suite</h1> +<h2 id="qunit-banner"></h2> +<div id="qunit-testrunner-toolbar"></div> +<h2 id="qunit-userAgent"></h2> +<ol id="qunit-tests"></ol> +<div id="qunit-fixture"> + +<div id="progressbar"></div> + +</div> +</body> +</html> diff --git a/apps/it/static/js/ui/tests/unit/progressbar/progressbar_common.js b/apps/it/static/js/ui/tests/unit/progressbar/progressbar_common.js new file mode 100644 index 0000000..0768576 --- /dev/null +++ b/apps/it/static/js/ui/tests/unit/progressbar/progressbar_common.js @@ -0,0 +1,12 @@ +TestHelpers.commonWidgetTests( "progressbar", { + defaults: { + disabled: false, + max: 100, + value: 0, + + //callbacks + change: null, + complete: null, + create: null + } +}); diff --git a/apps/it/static/js/ui/tests/unit/progressbar/progressbar_core.js b/apps/it/static/js/ui/tests/unit/progressbar/progressbar_core.js new file mode 100644 index 0000000..8f4a138 --- /dev/null +++ b/apps/it/static/js/ui/tests/unit/progressbar/progressbar_core.js @@ -0,0 +1,53 @@ +module( "progressbar: core" ); + +test( "markup structure", function() { + expect( 5 ); + var element = $( "#progressbar" ).progressbar(); + ok( element.hasClass( "ui-progressbar" ), "main element is .ui-progressbar" ); + ok( !element.hasClass( "ui-progressbar-indeterminate" ), + "main element is not .ui-progressbar-indeterminate" ); + equal( element.children().length, 1, "main element contains one child" ); + ok( element.children().eq( 0 ).hasClass( "ui-progressbar-value" ), + "child is .ui-progressbar-value" ); + equal( element.children().children().length, 0, "no overlay div" ); +}); + +test( "markup structure - indeterminate", function() { + expect( 5 ); + var element = $( "#progressbar" ).progressbar({ value: false }); + ok( element.hasClass( "ui-progressbar" ), "main element is .ui-progressbar" ); + ok( element.hasClass( "ui-progressbar-indeterminate" ), + "main element is .ui-progressbar-indeterminate" ); + equal( element.children().length, 1, "main element contains one child" ); + ok( element.children().eq( 0 ).hasClass( "ui-progressbar-value" ), + "child is .ui-progressbar-value" ); + equal( element.children().children( ".ui-progressbar-overlay" ).length, 1, + ".ui-progressbar-value has .ui-progressbar-overlay" ); +}); + +test( "accessibility", function() { + expect( 11 ); + var element = $( "#progressbar" ).progressbar(); + + equal( element.attr( "role" ), "progressbar", "aria role" ); + equal( element.attr( "aria-valuemin" ), 0, "aria-valuemin" ); + equal( element.attr( "aria-valuemax" ), 100, "aria-valuemax" ); + equal( element.attr( "aria-valuenow" ), 0, "aria-valuenow initially" ); + + element.progressbar( "value", 77 ); + equal( element.attr( "aria-valuenow" ), 77, "aria-valuenow" ); + + element.progressbar( "option", "max", 150 ); + equal( element.attr( "aria-valuemax" ), 150, "aria-valuemax" ); + + element.progressbar( "disable" ); + equal( element.attr( "aria-disabled" ), "true", "aria-disabled on" ); + + element.progressbar( "enable" ); + equal( element.attr( "aria-disabled" ), "false", "aria-disabled off" ); + + element.progressbar( "option", "value", false ); + equal( element.attr( "aria-valuemin" ), 0, "aria-valuemin" ); + equal( element.attr( "aria-valuemax" ), 150, "aria-valuemax" ); + strictEqual( element.attr( "aria-valuenow" ), undefined, "aria-valuenow" ); +}); diff --git a/apps/it/static/js/ui/tests/unit/progressbar/progressbar_events.js b/apps/it/static/js/ui/tests/unit/progressbar/progressbar_events.js new file mode 100644 index 0000000..95effda --- /dev/null +++ b/apps/it/static/js/ui/tests/unit/progressbar/progressbar_events.js @@ -0,0 +1,51 @@ +module( "progressbar: events" ); + +test( "create", function() { + expect( 1 ); + $( "#progressbar" ).progressbar({ + value: 5, + create: function() { + equal( $( this ).progressbar( "value" ), 5, "Correct value at create" ); + }, + change: function() { + ok( false, "create has triggered change()" ); + } + }); +}); + +test( "change", function() { + expect( 2 ); + var element = $( "#progressbar" ).progressbar(); + + element.one( "progressbarchange", function() { + equal( element.progressbar( "value" ), 5, "change triggered for middle value" ); + }); + element.progressbar( "value", 5 ); + element.one( "progressbarchange", function() { + equal( element.progressbar( "value" ), 100, "change triggered for final value" ); + }); + element.progressbar( "value", 100 ); +}); + +test( "complete", function() { + expect( 5 ); + var value, + changes = 0, + element = $( "#progressbar" ).progressbar({ + change: function() { + changes++; + equal( element.progressbar( "value" ), value, "change at " + value ); + }, + complete: function() { + equal( changes, 3, "complete triggered after change and not on indeterminate" ); + equal( element.progressbar( "value" ), 100, "value is 100" ); + } + }); + + value = 5; + element.progressbar( "value", value ); + value = false; + element.progressbar( "value", value ); + value = 100; + element.progressbar( "value", value ); +}); diff --git a/apps/it/static/js/ui/tests/unit/progressbar/progressbar_methods.js b/apps/it/static/js/ui/tests/unit/progressbar/progressbar_methods.js new file mode 100644 index 0000000..cf7faf6 --- /dev/null +++ b/apps/it/static/js/ui/tests/unit/progressbar/progressbar_methods.js @@ -0,0 +1,25 @@ +module( "progressbar: methods" ); + +test( "destroy", function() { + expect( 1 ); + domEqual( "#progressbar", function() { + $( "#progressbar" ).progressbar().progressbar( "destroy" ); + }); +}); + +test( "value", function() { + expect( 3 ); + + var element = $( "<div>" ).progressbar({ value: 20 }); + equal( element.progressbar( "value" ), 20, "correct value as getter" ); + strictEqual( element.progressbar( "value", 30 ), element, "chainable as setter" ); + equal( element.progressbar( "option", "value" ), 30, "correct value after setter" ); +}); + +test( "widget", function() { + expect( 2 ); + var element = $( "#progressbar" ).progressbar(), + widgetElement = element.progressbar( "widget" ); + equal( widgetElement.length, 1, "one element" ); + strictEqual( widgetElement[ 0 ], element[ 0 ], "same element" ); +}); diff --git a/apps/it/static/js/ui/tests/unit/progressbar/progressbar_options.js b/apps/it/static/js/ui/tests/unit/progressbar/progressbar_options.js new file mode 100644 index 0000000..bc0b5d0 --- /dev/null +++ b/apps/it/static/js/ui/tests/unit/progressbar/progressbar_options.js @@ -0,0 +1,72 @@ +module( "progressbar: options" ); + +test( "{ value: 0 }, default", function() { + expect( 1 ); + $( "#progressbar" ).progressbar(); + equal( $( "#progressbar" ).progressbar( "value" ), 0 ); +}); + +// Ticket #7231 - valueDiv should be hidden when value is at 0% +test( "value: visibility of valueDiv", function() { + expect( 4 ); + var element = $( "#progressbar" ).progressbar({ + value: 0 + }); + ok( element.children( ".ui-progressbar-value" ).is( ":hidden" ), + "valueDiv hidden when value is initialized at 0" ); + element.progressbar( "value", 1 ); + ok( element.children( ".ui-progressbar-value" ).is( ":visible" ), + "valueDiv visible when value is set to 1" ); + element.progressbar( "value", 100 ); + ok( element.children( ".ui-progressbar-value" ).is( ":visible" ), + "valueDiv visible when value is set to 100" ); + element.progressbar( "value", 0 ); + ok( element.children( ".ui-progressbar-value" ).is( ":hidden" ), + "valueDiv hidden when value is set to 0" ); +}); + +test( "{ value: 5 }", function() { + expect( 1 ); + $( "#progressbar" ).progressbar({ + value: 5 + }); + equal( $( "#progressbar" ).progressbar( "value" ), 5 ); +}); + +test( "{ value: -5 }", function() { + expect( 1 ); + $( "#progressbar" ).progressbar({ + value: -5 + }); + equal( $( "#progressbar" ).progressbar( "value" ), 0, + "value constrained at min" ); +}); + +test( "{ value: 105 }", function() { + expect( 1 ); + $( "#progressbar" ).progressbar({ + value: 105 + }); + equal( $( "#progressbar" ).progressbar( "value" ), 100, + "value constrained at max" ); +}); + +test( "{ value: 10, max: 5 }", function() { + expect( 1 ); + $("#progressbar").progressbar({ + max: 5, + value: 10 + }); + equal( $( "#progressbar" ).progressbar( "value" ), 5, + "value constrained at max" ); +}); + +test( "change max below value", function() { + expect( 1 ); + $("#progressbar").progressbar({ + max: 10, + value: 10 + }).progressbar( "option", "max", 5 ); + equal( $( "#progressbar" ).progressbar( "value" ), 5, + "value constrained at max" ); +}); diff --git a/apps/it/static/js/ui/tests/unit/qunit-composite.css b/apps/it/static/js/ui/tests/unit/qunit-composite.css new file mode 100644 index 0000000..c530436 --- /dev/null +++ b/apps/it/static/js/ui/tests/unit/qunit-composite.css @@ -0,0 +1,13 @@ +iframe.qunit-subsuite { + position: fixed; + bottom: 0; + left: 0; + + margin: 0; + padding: 0; + border-width: 1px 0 0; + height: 45%; + width: 100%; + + background: #fff; +} diff --git a/apps/it/static/js/ui/tests/unit/qunit-composite.js b/apps/it/static/js/ui/tests/unit/qunit-composite.js new file mode 100644 index 0000000..2e54540 --- /dev/null +++ b/apps/it/static/js/ui/tests/unit/qunit-composite.js @@ -0,0 +1,112 @@ +(function( QUnit ) { + +QUnit.extend( QUnit, { + testSuites: function( suites ) { + QUnit.begin(function() { + QUnit.initIframe(); + }); + + for ( var i = 0; i < suites.length; i++ ) { + QUnit.runSuite( suites[i] ); + } + + QUnit.done(function() { + this.iframe.style.display = "none"; + }); + }, + + runSuite: function( suite ) { + var path = suite; + + if ( QUnit.is( "object", suite ) ) { + path = suite.path; + suite = suite.name; + } + + asyncTest( suite, function() { + QUnit.iframe.setAttribute( "src", path ); + }); + }, + + initIframe: function() { + var body = document.body, + iframe = this.iframe = document.createElement( "iframe" ), + iframeWin; + + iframe.className = "qunit-subsuite"; + body.appendChild( iframe ); + + function onIframeLoad() { + var module, test, + count = 0; + + if (iframe.src === "") { + return; + } + + iframeWin.QUnit.moduleStart(function( data ) { + // capture module name for messages + module = data.name; + }); + + iframeWin.QUnit.testStart(function( data ) { + // capture test name for messages + test = data.name; + }); + iframeWin.QUnit.testDone(function() { + test = null; + }); + + iframeWin.QUnit.log(function( data ) { + if (test === null) { + return; + } + // pass all test details through to the main page + var message = module + ": " + test + ": " + data.message; + expect( ++count ); + QUnit.push( data.result, data.actual, data.expected, message ); + }); + + iframeWin.QUnit.done(function() { + // start the wrapper test from the main page + start(); + }); + } + QUnit.addEvent( iframe, "load", onIframeLoad ); + + iframeWin = iframe.contentWindow; + } +}); + +QUnit.testStart(function( data ) { + // update the test status to show which test suite is running + QUnit.id( "qunit-testresult" ).innerHTML = "Running " + data.name + "...<br> "; +}); + +QUnit.testDone(function() { + var i, + current = QUnit.id( this.config.current.id ), + children = current.children, + src = this.iframe.src; + + // undo the auto-expansion of failed tests + for ( i = 0; i < children.length; i++ ) { + if ( children[i].nodeName === "OL" ) { + children[i].style.display = "none"; + } + } + + QUnit.addEvent(current, "dblclick", function( e ) { + var target = e && e.target ? e.target : window.event.srcElement; + if ( target.nodeName.toLowerCase() === "span" || target.nodeName.toLowerCase() === "b" ) { + target = target.parentNode; + } + if ( window.location && target.nodeName.toLowerCase() === "strong" ) { + window.location = src; + } + }); + + current.getElementsByTagName("a")[0].href = src; +}); + +}( QUnit ) ); diff --git a/apps/it/static/js/ui/tests/unit/resizable/all.html b/apps/it/static/js/ui/tests/unit/resizable/all.html new file mode 100644 index 0000000..63bc458 --- /dev/null +++ b/apps/it/static/js/ui/tests/unit/resizable/all.html @@ -0,0 +1,30 @@ +<!doctype html> +<html lang="en"> +<head> + <meta charset="utf-8"> + <title>jQuery UI Resizable Test Suite</title> + + <script src="../../../jquery-1.10.2.js"></script> + + <link rel="stylesheet" href="../../../external/qunit.css"> + <link rel="stylesheet" href="../qunit-composite.css"> + <script src="../../../external/qunit.js"></script> + <script src="../qunit-composite.js"></script> + <script src="../subsuite.js"></script> + + <script> + testAllVersions( "resizable" ); + </script> +</head> +<body> + +<h1 id="qunit-header">jQuery UI Resizable Test Suite</h1> +<h2 id="qunit-banner"></h2> +<div id="qunit-testrunner-toolbar"></div> +<h2 id="qunit-userAgent"></h2> +<ol id="qunit-tests"></ol> +<div id="qunit-fixture"> + +</div> +</body> +</html> diff --git a/apps/it/static/js/ui/tests/unit/resizable/images/test.jpg b/apps/it/static/js/ui/tests/unit/resizable/images/test.jpg Binary files differnew file mode 100644 index 0000000..0175b13 --- /dev/null +++ b/apps/it/static/js/ui/tests/unit/resizable/images/test.jpg diff --git a/apps/it/static/js/ui/tests/unit/resizable/resizable.html b/apps/it/static/js/ui/tests/unit/resizable/resizable.html new file mode 100644 index 0000000..255c988 --- /dev/null +++ b/apps/it/static/js/ui/tests/unit/resizable/resizable.html @@ -0,0 +1,65 @@ +<!doctype html> +<html lang="en"> +<head> + <meta charset="utf-8"> + <title>jQuery UI Resizable Test Suite</title> + + <script src="../../jquery.js"></script> + <link rel="stylesheet" href="../../../external/qunit.css"> + <script src="../../../external/qunit.js"></script> + <script src="../../jquery.simulate.js"></script> + <script src="../testsuite.js"></script> + <script> + TestHelpers.loadResources({ + css: [ "ui.core", "ui.resizable" ], + js: [ + "ui/jquery.ui.core.js", + "ui/jquery.ui.widget.js", + "ui/jquery.ui.mouse.js", + "ui/jquery.ui.resizable.js" + ] + }); + </script> + + <script src="resizable_common.js"></script> + <script src="resizable_core.js"></script> + <script src="resizable_events.js"></script> + <script src="resizable_methods.js"></script> + <script src="resizable_options.js"></script> + <script src="resizable_test_helpers.js"></script> + + <script src="../swarminject.js"></script> + + <style> + #container { + width: 300px; + height: 200px; + } + #resizable1 { + background: green; + height: 100px; + width: 100px; + } + #resizable2 { + height: 100px; + width: 100px; + } + </style> +</head> +<body> + +<h1 id="qunit-header">jQuery UI Resizable Test Suite</h1> +<h2 id="qunit-banner"></h2> +<div id="qunit-testrunner-toolbar"></div> +<h2 id="qunit-userAgent"></h2> +<ol id="qunit-tests"></ol> +<div id="qunit-fixture"> + +<div id="container"> + <div id="resizable1">I'm a resizable.</div> +</div> +<img src="images/test.jpg" id="resizable2" alt="solid gray"> + +</div> +</body> +</html> diff --git a/apps/it/static/js/ui/tests/unit/resizable/resizable_common.js b/apps/it/static/js/ui/tests/unit/resizable/resizable_common.js new file mode 100644 index 0000000..c261ac5 --- /dev/null +++ b/apps/it/static/js/ui/tests/unit/resizable/resizable_common.js @@ -0,0 +1,30 @@ +TestHelpers.commonWidgetTests( "resizable", { + defaults: { + alsoResize: false, + animate: false, + animateDuration: "slow", + animateEasing: "swing", + aspectRatio: false, + autoHide: false, + cancel: "input,textarea,button,select,option", + containment: false, + delay: 0, + disabled: false, + distance: 1, + ghost: false, + grid: false, + handles: "e,s,se", + helper: false, + maxHeight: null, + maxWidth: null, + minHeight: 10, + minWidth: 10, + zIndex: 90, + + // callbacks + create: null, + resize: null, + start: null, + stop: null + } +}); diff --git a/apps/it/static/js/ui/tests/unit/resizable/resizable_core.js b/apps/it/static/js/ui/tests/unit/resizable/resizable_core.js new file mode 100644 index 0000000..b02e8b4 --- /dev/null +++ b/apps/it/static/js/ui/tests/unit/resizable/resizable_core.js @@ -0,0 +1,209 @@ +/* + * resizable_core.js + */ + +(function($) { + +module("resizable: core"); + +/* +test("element types", function() { + var typeNames = ("p,h1,h2,h3,h4,h5,h6,blockquote,ol,ul,dl,div,form" + + ",table,fieldset,address,ins,del,em,strong,q,cite,dfn,abbr" + + ",acronym,code,samp,kbd,var,img,object,hr" + + ",input,button,label,select,iframe").split(","); + + $.each(typeNames, function(i) { + var typeName = typeNames[i]; + el = $(document.createElement(typeName)).appendTo("body"); + (typeName == "table" && el.append("<tr><td>content</td></tr>")); + el.resizable(); + ok(true, "$('<" + typeName + "/>').resizable()"); + el.resizable("destroy"); + el.remove(); + }); +}); +*/ + +test("n", function() { + expect(4); + + var handle = ".ui-resizable-n", target = $("#resizable1").resizable({ handles: "all" }); + + TestHelpers.resizable.drag(handle, 0, -50); + equal( target.height(), 150, "compare height" ); + + TestHelpers.resizable.drag(handle, 0, 50); + equal( target.height(), 100, "compare height" ); + + equal( target[0].style.left, "", "left should not be modified" ); + equal( target[0].style.width, "", "width should not be modified" ); +}); + +test("s", function() { + expect(5); + + var handle = ".ui-resizable-s", target = $("#resizable1").resizable({ handles: "all" }); + + TestHelpers.resizable.drag(handle, 0, 50); + equal( target.height(), 150, "compare height" ); + + TestHelpers.resizable.drag(handle, 0, -50); + equal( target.height(), 100, "compare height" ); + + equal( target[0].style.top, "", "top should not be modified" ); + equal( target[0].style.left, "", "left should not be modified" ); + equal( target[0].style.width, "", "width should not be modified" ); +}); + +test("e", function() { + expect(5); + + var handle = ".ui-resizable-e", target = $("#resizable1").resizable({ handles: "all" }); + + TestHelpers.resizable.drag(handle, 50); + equal( target.width(), 150, "compare width"); + + TestHelpers.resizable.drag(handle, -50); + equal( target.width(), 100, "compare width" ); + + equal( target[0].style.height, "", "height should not be modified" ); + equal( target[0].style.top, "", "top should not be modified" ); + equal( target[0].style.left, "", "left should not be modified" ); +}); + +test("w", function() { + expect(4); + + var handle = ".ui-resizable-w", target = $("#resizable1").resizable({ handles: "all" }); + + TestHelpers.resizable.drag(handle, -50); + equal( target.width(), 150, "compare width" ); + + TestHelpers.resizable.drag(handle, 50); + equal( target.width(), 100, "compare width" ); + + equal( target[0].style.height, "", "height should not be modified" ); + equal( target[0].style.top, "", "top should not be modified" ); +}); + +test("ne", function() { + expect(5); + + var handle = ".ui-resizable-ne", target = $("#resizable1").css({ overflow: "hidden" }).resizable({ handles: "all" }); + + TestHelpers.resizable.drag(handle, -50, -50); + equal( target.width(), 50, "compare width" ); + equal( target.height(), 150, "compare height" ); + + TestHelpers.resizable.drag(handle, 50, 50); + equal( target.width(), 100, "compare width" ); + equal( target.height(), 100, "compare height" ); + + equal( target[0].style.left, "", "left should not be modified" ); +}); + +test("se", function() { + expect(6); + + var handle = ".ui-resizable-se", target = $("#resizable1").resizable({ handles: "all" }); + + TestHelpers.resizable.drag(handle, 50, 50); + equal( target.width(), 150, "compare width" ); + equal( target.height(), 150, "compare height" ); + + TestHelpers.resizable.drag(handle, -50, -50); + equal( target.width(), 100, "compare width" ); + equal( target.height(), 100, "compare height" ); + + equal( target[0].style.top, "", "top should not be modified" ); + equal( target[0].style.left, "", "left should not be modified" ); +}); + +test("sw", function() { + expect(5); + + var handle = ".ui-resizable-sw", target = $("#resizable1").resizable({ handles: "all" }); + + TestHelpers.resizable.drag(handle, -50, -50); + equal( target.width(), 150, "compare width" ); + equal( target.height(), 50, "compare height" ); + + TestHelpers.resizable.drag(handle, 50, 50); + equal( target.width(), 100, "compare width" ); + equal( target.height(), 100, "compare height" ); + + equal( target[0].style.top, "", "top should not be modified" ); +}); + +test("nw", function() { + expect(4); + + var handle = ".ui-resizable-nw", target = $("#resizable1").resizable({ handles: "all" }); + + TestHelpers.resizable.drag(handle, -50, -50); + equal( target.width(), 150, "compare width" ); + equal( target.height(), 150, "compare height" ); + + TestHelpers.resizable.drag(handle, 50, 50); + equal( target.width(), 100, "compare width" ); + equal( target.height(), 100, "compare height" ); +}); + +test("handle with complex markup (#8756)", function() { + expect(2); + + $("#resizable1") + .append( + $("<div>") + .addClass("ui-resizable-handle") + .addClass("ui-resizable-w") + .append($("<div>")) + ); + + var handle = ".ui-resizable-w div", target = $("#resizable1").resizable({ handles: "all" }); + + TestHelpers.resizable.drag(handle, -50); + equal( target.width(), 150, "compare width" ); + + TestHelpers.resizable.drag(handle, 50); + equal( target.width(), 100, "compare width" ); +}); + +test("resizable accounts for scroll position correctly (#3815)", function() { + expect( 3 ); + + var position, top, left, + container = $("<div style='overflow:scroll;height:300px;width:300px;position:relative;'></div>").appendTo("#qunit-fixture"), + overflowed = $("<div style='width: 1000px; height: 1000px;'></div>").appendTo( container ), + el = $("<div style='height:100px;width:100px;position:absolute;top:10px;left:10px;'></div>").appendTo( overflowed ).resizable({ handles: "all" }), + handle = ".ui-resizable-e"; + + container.scrollLeft( 100 ).scrollTop( 100 ); + + position = el.position(); + left = el.css("left"); + top = el.css("top"); + + TestHelpers.resizable.drag(handle, 50, 50); + deepEqual( el.position(), position, "position stays the same when resized" ); + equal( el.css("left"), left, "css('left') stays the same when resized" ); + equal( el.css("top"), top, "css('top') stays the same when resized" ); +}); + +test( "resizable stores correct size when using helper and grid (#9547)", function() { + expect( 2 ); + + var handle = ".ui-resizable-se", + target = $( "#resizable1" ).resizable({ + handles: "all", + helper: "ui-resizable-helper", + grid: [ 10, 10 ] + }); + + TestHelpers.resizable.drag( handle, 1, 1 ); + equal( target.width(), 100, "compare width" ); + equal( target.height(), 100, "compare height" ); +}); + +})(jQuery); diff --git a/apps/it/static/js/ui/tests/unit/resizable/resizable_events.js b/apps/it/static/js/ui/tests/unit/resizable/resizable_events.js new file mode 100644 index 0000000..18e25bf --- /dev/null +++ b/apps/it/static/js/ui/tests/unit/resizable/resizable_events.js @@ -0,0 +1,220 @@ +/* + * resizable_events.js + */ +(function($) { + +module("resizable: events"); + +test("start", function() { + + expect(5); + + var count = 0, + handle = ".ui-resizable-se"; + + $("#resizable1").resizable({ + handles: "all", + start: function(event, ui) { + equal( ui.size.width, 100, "compare width" ); + equal( ui.size.height, 100, "compare height" ); + equal( ui.originalSize.width, 100, "compare original width" ); + equal( ui.originalSize.height, 100, "compare original height" ); + count++; + } + }); + + TestHelpers.resizable.drag(handle, 50, 50); + + equal(count, 1, "start callback should happen exactly once"); + +}); + +test( "resize", function() { + + expect( 9 ); + + var count = 0, + handle = ".ui-resizable-se"; + + $("#resizable1").resizable({ + handles: "all", + resize: function( event, ui ) { + if ( count === 0 ) { + equal( ui.size.width, 125, "compare width" ); + equal( ui.size.height, 125, "compare height" ); + equal( ui.originalSize.width, 100, "compare original width" ); + equal( ui.originalSize.height, 100, "compare original height" ); + } else { + equal( ui.size.width, 150, "compare width" ); + equal( ui.size.height, 150, "compare height" ); + equal( ui.originalSize.width, 100, "compare original width" ); + equal( ui.originalSize.height, 100, "compare original height" ); + } + count++; + } + }); + + TestHelpers.resizable.drag( handle, 50, 50 ); + + equal( count, 2, "resize callback should happen exactly once per size adjustment" ); + +}); + +test( "resize (min/max dimensions)", function() { + + expect( 5 ); + + var count = 0, + handle = ".ui-resizable-se"; + + $("#resizable1").resizable({ + handles: "all", + minWidth: 60, + minHeight: 60, + maxWidth: 100, + maxHeight: 100, + resize: function( event, ui ) { + equal( ui.size.width, 60, "compare width" ); + equal( ui.size.height, 60, "compare height" ); + equal( ui.originalSize.width, 100, "compare original width" ); + equal( ui.originalSize.height, 100, "compare original height" ); + count++; + } + }); + + TestHelpers.resizable.drag( handle, -200, -200 ); + + equal( count, 1, "resize callback should happen exactly once per size adjustment" ); + +}); + +test( "resize (containment)", function() { + + expect( 5 ); + + var count = 0, + handle = ".ui-resizable-se", + container = $("#resizable1").wrap("<div>").parent().css({ + height: "100px", + width: "100px" + }); + + $("#resizable1").resizable({ + handles: "all", + containment: container, + resize: function( event, ui ) { + equal( ui.size.width, 10, "compare width" ); + equal( ui.size.height, 10, "compare height" ); + equal( ui.originalSize.width, 100, "compare original width" ); + equal( ui.originalSize.height, 100, "compare original height" ); + count++; + } + }); + + // Prove you can't resize outside containment by dragging southeast corner southeast + TestHelpers.resizable.drag( handle, 100, 100 ); + + // Prove you can't resize outside containment by dragging southeast corner northwest + TestHelpers.resizable.drag( handle, -200, -200 ); + + equal( count, 1, "resize callback should happen exactly once per size adjustment" ); + +}); + +test("resize (grid)", function() { + + expect(5); + + var count = 0, + handle = ".ui-resizable-se"; + + $("#resizable1").resizable({ + handles: "all", + grid: 50, + resize: function(event, ui) { + equal( ui.size.width, 150, "compare width" ); + equal( ui.size.height, 150, "compare height" ); + equal( ui.originalSize.width, 100, "compare original width" ); + equal( ui.originalSize.height, 100, "compare original height" ); + count++; + } + }); + + TestHelpers.resizable.drag(handle, 50, 50); + + equal(count, 1, "resize callback should happen exactly once per grid-unit size adjustment"); + +}); + +test("stop", function() { + + expect(5); + + var count = 0, + handle = ".ui-resizable-se"; + + $("#resizable1").resizable({ + handles: "all", + stop: function(event, ui) { + equal( ui.size.width, 150, "compare width" ); + equal( ui.size.height, 150, "compare height" ); + equal( ui.originalSize.width, 100, "compare original width" ); + equal( ui.originalSize.height, 100, "compare original height" ); + count++; + } + }); + + TestHelpers.resizable.drag(handle, 50, 50); + + equal(count, 1, "stop callback should happen exactly once"); + +}); + +test( "resize (containment) works with parent with negative offset", function() { + + expect( 1 ); + + var widthBefore, widthAfter, + handle = ".ui-resizable-e", + target = $( "#resizable1" ), + absoluteContainer = target.wrap( "<div />" ).parent(), + fixedContainer = absoluteContainer.wrap( "<div />" ).parent(), + increaseWidthBy = 50; + + // position fixed container in window top left + fixedContainer.css({ + width: 400, + height: 100, + position: "fixed", + top: 0, + left: 0 + }); + + // position absolute container within fixed on slightly outside window + absoluteContainer.css({ + width: 400, + height: 100, + position: "absolute", + top: 0, + left: -50 + }); + + // set up resizable to be contained within absolute container + target.resizable({ + handles: "all", + containment: "parent" + }).css({ + width: 300 + }); + + widthBefore = target.width(); + + TestHelpers.resizable.drag( handle, increaseWidthBy, 0 ); + + widthAfter = target.width(); + + equal( widthAfter, ( widthBefore + increaseWidthBy ), "resizable width should be increased by the value dragged" ); + +}); + +})(jQuery); diff --git a/apps/it/static/js/ui/tests/unit/resizable/resizable_methods.js b/apps/it/static/js/ui/tests/unit/resizable/resizable_methods.js new file mode 100644 index 0000000..b12f303 --- /dev/null +++ b/apps/it/static/js/ui/tests/unit/resizable/resizable_methods.js @@ -0,0 +1,12 @@ +/* + * resizable_methods.js + */ +(function($) { + +module("resizable: methods"); + +// this is here to make JSHint pass "unused", and we don't want to +// remove the parameter for when we finally implement +$.noop(); + +})(jQuery); diff --git a/apps/it/static/js/ui/tests/unit/resizable/resizable_options.js b/apps/it/static/js/ui/tests/unit/resizable/resizable_options.js new file mode 100644 index 0000000..e739d61 --- /dev/null +++ b/apps/it/static/js/ui/tests/unit/resizable/resizable_options.js @@ -0,0 +1,291 @@ +/* + * resizable_options.js + */ +(function($) { + +module("resizable: options"); + +test( "alsoResize", function() { + expect( 2 ); + + var other = $( "<div>" ) + .css({ + width: 50, + height: 50 + }) + .appendTo( "body" ), + element = $( "#resizable1" ).resizable({ + alsoResize: other + }), + handle = ".ui-resizable-e"; + + TestHelpers.resizable.drag( handle, 80 ); + equal( element.width(), 180, "resizable width" ); + equal( other.width(), 130, "alsoResize width" ); +}); + + +test("aspectRatio: 'preserve' (e)", function() { + expect(4); + + var handle = ".ui-resizable-e", target = $("#resizable1").resizable({ aspectRatio: "preserve", handles: "all", minWidth: 70, minHeight: 50, maxWidth: 150, maxHeight: 130 }); + + TestHelpers.resizable.drag(handle, 80); + equal( target.width(), 130, "compare maxWidth"); + equal( target.height(), 130, "compare maxHeight"); + + TestHelpers.resizable.drag(handle, -130); + equal( target.width(), 70, "compare minWidth"); + equal( target.height(), 70, "compare minHeight"); +}); + +test("aspectRatio: 'preserve' (w)", function() { + expect(4); + + var handle = ".ui-resizable-w", target = $("#resizable1").resizable({ aspectRatio: "preserve", handles: "all", minWidth: 70, minHeight: 50, maxWidth: 150, maxHeight: 130 }); + + TestHelpers.resizable.drag(handle, -80); + equal( target.width(), 130, "compare maxWidth"); + equal( target.height(), 130, "compare maxHeight"); + + TestHelpers.resizable.drag(handle, 130); + equal( target.width(), 70, "compare minWidth"); + equal( target.height(), 70, "compare minHeight"); +}); + +test("aspectRatio: 'preserve' (n)", function() { + expect(4); + + var handle = ".ui-resizable-n", target = $("#resizable1").resizable({ aspectRatio: "preserve", handles: "all", minWidth: 70, minHeight: 50, maxWidth: 150, maxHeight: 130 }); + + TestHelpers.resizable.drag(handle, 0, -80); + equal( target.width(), 130, "compare maxWidth"); + equal( target.height(), 130, "compare maxHeight"); + + TestHelpers.resizable.drag(handle, 0, 80); + equal( target.width(), 70, "compare minWidth"); + equal( target.height(), 70, "compare minHeight"); +}); + +test("aspectRatio: 'preserve' (s)", function() { + expect(4); + + var handle = ".ui-resizable-s", target = $("#resizable1").resizable({ aspectRatio: "preserve", handles: "all", minWidth: 70, minHeight: 50, maxWidth: 150, maxHeight: 130 }); + + TestHelpers.resizable.drag(handle, 0, 80); + equal( target.width(), 130, "compare maxWidth"); + equal( target.height(), 130, "compare maxHeight"); + + TestHelpers.resizable.drag(handle, 0, -80); + equal( target.width(), 70, "compare minWidth"); + equal( target.height(), 70, "compare minHeight"); +}); + +test("aspectRatio: 'preserve' (se)", function() { + expect(4); + + var handle = ".ui-resizable-se", target = $("#resizable1").resizable({ aspectRatio: "preserve", handles: "all", minWidth: 70, minHeight: 50, maxWidth: 150, maxHeight: 130 }); + + TestHelpers.resizable.drag(handle, 80, 80); + equal( target.width(), 130, "compare maxWidth"); + equal( target.height(), 130, "compare maxHeight"); + + TestHelpers.resizable.drag(handle, -80, -80); + equal( target.width(), 70, "compare minWidth"); + equal( target.height(), 70, "compare minHeight"); +}); + +test("aspectRatio: 'preserve' (sw)", function() { + expect(4); + + var handle = ".ui-resizable-sw", target = $("#resizable1").resizable({ aspectRatio: "preserve", handles: "all", minWidth: 70, minHeight: 50, maxWidth: 150, maxHeight: 130 }); + + TestHelpers.resizable.drag(handle, -80, 80); + equal( target.width(), 130, "compare maxWidth"); + equal( target.height(), 130, "compare maxHeight"); + + TestHelpers.resizable.drag(handle, 80, -80); + equal( target.width(), 70, "compare minWidth"); + equal( target.height(), 70, "compare minHeight"); +}); + +test("aspectRatio: 'preserve' (ne)", function() { + expect(4); + + var handle = ".ui-resizable-ne", target = $("#resizable1").resizable({ aspectRatio: "preserve", handles: "all", minWidth: 70, minHeight: 50, maxWidth: 150, maxHeight: 130 }); + + TestHelpers.resizable.drag(handle, 80, -80); + equal( target.width(), 130, "compare maxWidth"); + equal( target.height(), 130, "compare maxHeight"); + + TestHelpers.resizable.drag(handle, -80, 80); + equal( target.width(), 70, "compare minWidth"); + equal( target.height(), 70, "compare minHeight"); +}); + +test( "containment", function() { + expect( 4 ); + var element = $( "#resizable1" ).resizable({ + containment: "#container" + }); + + TestHelpers.resizable.drag( ".ui-resizable-se", 20, 30 ); + equal( element.width(), 120, "unconstrained width within container" ); + equal( element.height(), 130, "unconstrained height within container" ); + + TestHelpers.resizable.drag( ".ui-resizable-se", 400, 400 ); + equal( element.width(), 300, "constrained width at containment edge" ); + equal( element.height(), 200, "constrained height at containment edge" ); +}); + +test("grid", function() { + expect(4); + + var handle = ".ui-resizable-se", target = $("#resizable1").resizable({ handles: "all", grid: [0, 20] }); + + TestHelpers.resizable.drag(handle, 3, 9); + equal( target.width(), 103, "compare width"); + equal( target.height(), 100, "compare height"); + + TestHelpers.resizable.drag(handle, 15, 11); + equal( target.width(), 118, "compare width"); + equal( target.height(), 120, "compare height"); +}); + +test("grid (min/max dimensions)", function() { + expect(4); + + var handle = ".ui-resizable-se", target = $("#resizable1").resizable({ handles: "all", grid: 20, minWidth: 65, minHeight: 65, maxWidth: 135, maxHeight: 135 }); + + TestHelpers.resizable.drag(handle, 50, 50); + equal( target.width(), 120, "grid should respect maxWidth"); + equal( target.height(), 120, "grid should respect maxHeight"); + + TestHelpers.resizable.drag(handle, -100, -100); + equal( target.width(), 80, "grid should respect minWidth"); + equal( target.height(), 80, "grid should respect minHeight"); +}); + +test("grid (wrapped)", function() { + expect(4); + + var handle = ".ui-resizable-se", target = $("#resizable2").resizable({ handles: "all", grid: [0, 20] }); + + TestHelpers.resizable.drag(handle, 3, 9); + equal( target.width(), 103, "compare width"); + equal( target.height(), 100, "compare height"); + + TestHelpers.resizable.drag(handle, 15, 11); + equal( target.width(), 118, "compare width"); + equal( target.height(), 120, "compare height"); +}); + +test( "grid - Resizable: can be moved when grid option is set (#9611)", function() { + expect( 6 ); + + var oldPosition, + handle = ".ui-resizable-nw", + target = $( "#resizable1" ).resizable({ + handles: "all", + grid: 50 + }); + + TestHelpers.resizable.drag( handle, 50, 50 ); + equal( target.width(), 50, "compare width" ); + equal( target.height(), 50, "compare height" ); + + oldPosition = target.position(); + + TestHelpers.resizable.drag( handle, 50, 50 ); + equal( target.width(), 50, "compare width" ); + equal( target.height(), 50, "compare height" ); + equal( target.position().top, oldPosition.top, "compare top" ); + equal( target.position().left, oldPosition.left, "compare left" ); +}); + +test("ui-resizable-se { handles: 'all', minWidth: 60, minHeight: 60, maxWidth: 100, maxHeight: 100 }", function() { + expect(4); + + var handle = ".ui-resizable-se", target = $("#resizable1").resizable({ handles: "all", minWidth: 60, minHeight: 60, maxWidth: 100, maxHeight: 100 }); + + TestHelpers.resizable.drag(handle, -50, -50); + equal( target.width(), 60, "compare minWidth" ); + equal( target.height(), 60, "compare minHeight" ); + + TestHelpers.resizable.drag(handle, 70, 70); + equal( target.width(), 100, "compare maxWidth" ); + equal( target.height(), 100, "compare maxHeight" ); +}); + +test("ui-resizable-sw { handles: 'all', minWidth: 60, minHeight: 60, maxWidth: 100, maxHeight: 100 }", function() { + expect(4); + + var handle = ".ui-resizable-sw", target = $("#resizable1").resizable({ handles: "all", minWidth: 60, minHeight: 60, maxWidth: 100, maxHeight: 100 }); + + TestHelpers.resizable.drag(handle, 50, -50); + equal( target.width(), 60, "compare minWidth" ); + equal( target.height(), 60, "compare minHeight" ); + + TestHelpers.resizable.drag(handle, -70, 70); + equal( target.width(), 100, "compare maxWidth" ); + equal( target.height(), 100, "compare maxHeight" ); +}); + +test("ui-resizable-ne { handles: 'all', minWidth: 60, minHeight: 60, maxWidth: 100, maxHeight: 100 }", function() { + expect(4); + + var handle = ".ui-resizable-ne", target = $("#resizable1").resizable({ handles: "all", minWidth: 60, minHeight: 60, maxWidth: 100, maxHeight: 100 }); + + TestHelpers.resizable.drag(handle, -50, 50); + equal( target.width(), 60, "compare minWidth" ); + equal( target.height(), 60, "compare minHeight" ); + + TestHelpers.resizable.drag(handle, 70, -70); + equal( target.width(), 100, "compare maxWidth" ); + equal( target.height(), 100, "compare maxHeight" ); +}); + +test("ui-resizable-nw { handles: 'all', minWidth: 60, minHeight: 60, maxWidth: 100, maxHeight: 100 }", function() { + expect(4); + + var handle = ".ui-resizable-nw", target = $("#resizable1").resizable({ handles: "all", minWidth: 60, minHeight: 60, maxWidth: 100, maxHeight: 100 }); + + TestHelpers.resizable.drag(handle, 70, 70); + equal( target.width(), 60, "compare minWidth" ); + equal( target.height(), 60, "compare minHeight" ); + + TestHelpers.resizable.drag(handle, -70, -70); + equal( target.width(), 100, "compare maxWidth" ); + equal( target.height(), 100, "compare maxHeight" ); +}); + +test("zIndex, applied to all handles", function() { + expect(8); + + var target = $("<div></div>").resizable({ handles: "all", zIndex: 100 }); + target.children( ".ui-resizable-handle" ).each( function( index, handle ) { + equal( $( handle ).css( "zIndex" ), 100, "compare zIndex" ); + }); +}); + +test( "alsoResize + containment", function() { + expect( 4 ); + var other = $( "<div>" ) + .css({ + width: 50, + height: 50 + }) + .appendTo( "body" ), + element = $( "#resizable1" ).resizable({ + alsoResize: other, + containment: "#container" + }); + + TestHelpers.resizable.drag( ".ui-resizable-se", 400, 400 ); + equal( element.width(), 300, "resizable constrained width at containment edge" ); + equal( element.height(), 200, "resizable constrained height at containment edge" ); + equal( other.width(), 250, "alsoResize constrained width at containment edge" ); + equal( other.height(), 150, "alsoResize constrained height at containment edge" ); +}); + +})(jQuery); diff --git a/apps/it/static/js/ui/tests/unit/resizable/resizable_test_helpers.js b/apps/it/static/js/ui/tests/unit/resizable/resizable_test_helpers.js new file mode 100644 index 0000000..7ab5aa1 --- /dev/null +++ b/apps/it/static/js/ui/tests/unit/resizable/resizable_test_helpers.js @@ -0,0 +1,11 @@ +TestHelpers.resizable = { + drag: function( el, dx, dy ) { + // this mouseover is to work around a limitation in resizable + // TODO: fix resizable so handle doesn't require mouseover in order to be used + $( el ).simulate("mouseover").simulate( "drag", { + moves: 2, + dx: dx, + dy: dy + }); + } +};
\ No newline at end of file diff --git a/apps/it/static/js/ui/tests/unit/selectable/all.html b/apps/it/static/js/ui/tests/unit/selectable/all.html new file mode 100644 index 0000000..5ee323f --- /dev/null +++ b/apps/it/static/js/ui/tests/unit/selectable/all.html @@ -0,0 +1,30 @@ +<!doctype html> +<html lang="en"> +<head> + <meta charset="utf-8"> + <title>jQuery UI Selectable Test Suite</title> + + <script src="../../../jquery-1.10.2.js"></script> + + <link rel="stylesheet" href="../../../external/qunit.css"> + <link rel="stylesheet" href="../qunit-composite.css"> + <script src="../../../external/qunit.js"></script> + <script src="../qunit-composite.js"></script> + <script src="../subsuite.js"></script> + + <script> + testAllVersions( "selectable" ); + </script> +</head> +<body> + +<h1 id="qunit-header">jQuery UI Selectable Test Suite</h1> +<h2 id="qunit-banner"></h2> +<div id="qunit-testrunner-toolbar"></div> +<h2 id="qunit-userAgent"></h2> +<ol id="qunit-tests"></ol> +<div id="qunit-fixture"> + +</div> +</body> +</html> diff --git a/apps/it/static/js/ui/tests/unit/selectable/selectable.html b/apps/it/static/js/ui/tests/unit/selectable/selectable.html new file mode 100644 index 0000000..18ffc6e --- /dev/null +++ b/apps/it/static/js/ui/tests/unit/selectable/selectable.html @@ -0,0 +1,51 @@ +<!doctype html> +<html lang="en"> +<head> + <meta charset="utf-8"> + <title>jQuery UI Selectable Test Suite</title> + + <script src="../../jquery.js"></script> + <link rel="stylesheet" href="../../../external/qunit.css"> + <script src="../../../external/qunit.js"></script> + <script src="../../jquery.simulate.js"></script> + <script src="../testsuite.js"></script> + <script> + TestHelpers.loadResources({ + css: [ "ui.core", "ui.selectable" ], + js: [ + "ui/jquery.ui.core.js", + "ui/jquery.ui.widget.js", + "ui/jquery.ui.mouse.js", + "ui/jquery.ui.selectable.js" + ] + }); + </script> + + <script src="selectable_common.js"></script> + <script src="selectable_core.js"></script> + <script src="selectable_events.js"></script> + <script src="selectable_methods.js"></script> + <script src="selectable_options.js"></script> + + <script src="../swarminject.js"></script> +</head> +<body> + +<h1 id="qunit-header">jQuery UI Selectable Test Suite</h1> +<h2 id="qunit-banner"></h2> +<div id="qunit-testrunner-toolbar"></div> +<h2 id="qunit-userAgent"></h2> +<ol id="qunit-tests"></ol> +<div id="qunit-fixture"> + +<ul id="selectable1"> + <li>Item 1</li> + <li>Item 2</li> + <li class="special">Item 3</li> + <li>Item 4</li> + <li>Item 5</li> +</ul> + +</div> +</body> +</html> diff --git a/apps/it/static/js/ui/tests/unit/selectable/selectable_common.js b/apps/it/static/js/ui/tests/unit/selectable/selectable_common.js new file mode 100644 index 0000000..d00a47b --- /dev/null +++ b/apps/it/static/js/ui/tests/unit/selectable/selectable_common.js @@ -0,0 +1,21 @@ +TestHelpers.commonWidgetTests("selectable", { + defaults: { + appendTo: "body", + autoRefresh: true, + cancel: "input,textarea,button,select,option", + delay: 0, + disabled: false, + distance: 0, + filter: "*", + tolerance: "touch", + + // callbacks + create: null, + selected: null, + selecting: null, + start: null, + stop: null, + unselected: null, + unselecting: null + } +}); diff --git a/apps/it/static/js/ui/tests/unit/selectable/selectable_core.js b/apps/it/static/js/ui/tests/unit/selectable/selectable_core.js new file mode 100644 index 0000000..9953b6c --- /dev/null +++ b/apps/it/static/js/ui/tests/unit/selectable/selectable_core.js @@ -0,0 +1,3 @@ +/* + * selectable_core.js + */
\ No newline at end of file diff --git a/apps/it/static/js/ui/tests/unit/selectable/selectable_events.js b/apps/it/static/js/ui/tests/unit/selectable/selectable_events.js new file mode 100644 index 0000000..ae35fa3 --- /dev/null +++ b/apps/it/static/js/ui/tests/unit/selectable/selectable_events.js @@ -0,0 +1,69 @@ +/* + * selectable_events.js + */ +(function( $ ) { + +module("selectable: events"); + +test( "start", function() { + expect( 2 ); + var el = $("#selectable1"); + el.selectable({ + start: function() { + ok( true, "drag fired start callback" ); + equal( this, el[0], "context of callback" ); + } + }); + el.simulate( "drag", { + dx: 20, + dy: 20 + }); +}); + +test( "stop", function() { + expect( 2 ); + var el = $("#selectable1"); + el.selectable({ + start: function() { + ok( true, "drag fired stop callback" ); + equal( this, el[0], "context of callback" ); + } + }); + el.simulate( "drag", { + dx: 20, + dy: 20 + }); +}); + +test( "mousedown: initial position of helper", function() { + expect( 2 ); + + var helperOffset, + element = $( "#selectable1" ).selectable(), + contentToForceScroll = $( "<div>" ).css({ + height: "10000px", + width: "10000px" + }); + + contentToForceScroll.appendTo( "body" ); + $( window ).scrollTop( 100 ).scrollLeft( 100 ); + + element.simulate( "mousedown", { + clientX: 10, + clientY: 10 + }); + + // we do a GTE comparison here because IE7 erroneously subtracts + // 2 pixels from a simulated mousedown for clientX/Y + // Support: IE7 + helperOffset = $( ".ui-selectable-helper" ).offset(); + ok( helperOffset.top >= 99, "Scroll top should be accounted for." ); + ok( helperOffset.left >= 99, "Scroll left should be accounted for." ); + + // Cleanup + element.simulate( "mouseup" ); + contentToForceScroll.remove(); + $( window ).scrollTop( 0 ).scrollLeft( 0 ); +}); + +})( jQuery ); diff --git a/apps/it/static/js/ui/tests/unit/selectable/selectable_methods.js b/apps/it/static/js/ui/tests/unit/selectable/selectable_methods.js new file mode 100644 index 0000000..72f9bb2 --- /dev/null +++ b/apps/it/static/js/ui/tests/unit/selectable/selectable_methods.js @@ -0,0 +1,104 @@ +/* + * selectable_methods.js + */ +(function($) { + +module("selectable: methods"); + +test("init", function() { + expect( 5 ); + + $("<div></div>").appendTo("body").selectable().remove(); + ok(true, ".selectable() called on element"); + + $([]).selectable().remove(); + ok(true, ".selectable() called on empty collection"); + + $("<div></div>").selectable().remove(); + ok(true, ".selectable() called on disconnected DOMElement"); + + var el = $("<div></div>").selectable(); + el.selectable("option", "foo"); + el.remove(); + ok(true, "arbitrary option getter after init"); + + $("<div></div>").selectable().selectable("option", "foo", "bar").remove(); + ok(true, "arbitrary option setter after init"); +}); + +test("destroy", function() { + expect( 4 ); + + $("<div></div>").appendTo("body").selectable().selectable("destroy").remove(); + ok(true, ".selectable('destroy') called on element"); + + $([]).selectable().selectable("destroy").remove(); + ok(true, ".selectable('destroy') called on empty collection"); + + $("<div></div>").selectable().selectable("destroy").remove(); + ok(true, ".selectable('destroy') called on disconnected DOMElement"); + + var expected = $("<div></div>").selectable(), + actual = expected.selectable("destroy"); + equal(actual, expected, "destroy is chainable"); +}); + +test("enable", function() { + expect(3); + var expected, actual, + fired = false, + el = $("#selectable1"); + + el.selectable({ + disabled: true, + start: function() { fired = true; } + }); + el.simulate( "drag", { + dx: 20, + dy: 20 + }); + equal(fired, false, "start fired"); + el.selectable("enable"); + el.simulate( "drag", { + dx: 20, + dy: 20 + }); + equal(fired, true, "start fired"); + el.selectable("destroy"); + + expected = $("<div></div>").selectable(); + actual = expected.selectable("enable"); + equal(actual, expected, "enable is chainable"); +}); + +test("disable", function() { + expect(3); + var expected, actual, + fired = false, + el = $("#selectable1"); + + el.selectable({ + disabled: false, + start: function() { fired = true; } + }); + el.simulate( "drag", { + dx: 20, + dy: 20 + }); + equal(fired, true, "start fired"); + el.selectable("disable"); + fired = false; + + el.simulate( "drag", { + dx: 20, + dy: 20 + }); + equal(fired, false, "start fired"); + el.selectable("destroy"); + + expected = $("<div></div>").selectable(); + actual = expected.selectable("disable"); + equal(actual, expected, "disable is chainable"); +}); + +})(jQuery); diff --git a/apps/it/static/js/ui/tests/unit/selectable/selectable_options.js b/apps/it/static/js/ui/tests/unit/selectable/selectable_options.js new file mode 100644 index 0000000..973247f --- /dev/null +++ b/apps/it/static/js/ui/tests/unit/selectable/selectable_options.js @@ -0,0 +1,65 @@ +/* + * selectable_options.js + */ +(function($) { + +module("selectable: options"); + +test("autoRefresh", function() { + expect(3); + + var actual = 0, + el = $("#selectable1"), + sel = $("*", el), + selected = function() { actual += 1; }; + + el = $("#selectable1").selectable({ autoRefresh: false, selected: selected }); + sel.hide(); + el.simulate( "drag", { + dx: 1000, + dy: 1000 + }); + equal(actual, sel.length); + el.selectable("destroy"); + + actual = 0; + sel.show(); + el = $("#selectable1").selectable({ autoRefresh: true, selected: selected }); + sel.hide(); + el.simulate( "drag", { + dx: 1000, + dy: 1000 + }); + equal(actual, 0); + + sel.show(); + $( sel[ 0 ] ).simulate( "drag", { + dx: 1000, + dy: 1000 + }); + equal(actual, sel.length); + + el.selectable("destroy"); + sel.show(); +}); + +test("filter", function() { + expect(2); + + var actual =0, + el = $("#selectable1"), + sel = $("*", el), + selected = function() { actual += 1; }; + + + el = $("#selectable1").selectable({ filter: ".special", selected: selected }); + el.simulate( "drag", { + dx: 1000, + dy: 1000 + }); + ok(sel.length !== 1, "this test assumes more than 1 selectee"); + equal(actual, 1); + el.selectable("destroy"); +}); + +})(jQuery); diff --git a/apps/it/static/js/ui/tests/unit/slider/all.html b/apps/it/static/js/ui/tests/unit/slider/all.html new file mode 100644 index 0000000..a447b98 --- /dev/null +++ b/apps/it/static/js/ui/tests/unit/slider/all.html @@ -0,0 +1,30 @@ +<!doctype html> +<html lang="en"> +<head> + <meta charset="utf-8"> + <title>jQuery UI Slider Test Suite</title> + + <script src="../../../jquery-1.10.2.js"></script> + + <link rel="stylesheet" href="../../../external/qunit.css"> + <link rel="stylesheet" href="../qunit-composite.css"> + <script src="../../../external/qunit.js"></script> + <script src="../qunit-composite.js"></script> + <script src="../subsuite.js"></script> + + <script> + testAllVersions( "slider" ); + </script> +</head> +<body> + +<h1 id="qunit-header">jQuery UI Slider Test Suite</h1> +<h2 id="qunit-banner"></h2> +<div id="qunit-testrunner-toolbar"></div> +<h2 id="qunit-userAgent"></h2> +<ol id="qunit-tests"></ol> +<div id="qunit-fixture"> + +</div> +</body> +</html> diff --git a/apps/it/static/js/ui/tests/unit/slider/slider.html b/apps/it/static/js/ui/tests/unit/slider/slider.html new file mode 100644 index 0000000..98adfa3 --- /dev/null +++ b/apps/it/static/js/ui/tests/unit/slider/slider.html @@ -0,0 +1,48 @@ +<!doctype html> +<html lang="en"> +<head> + <meta charset="utf-8"> + <title>jQuery UI Slider Test Suite</title> + + <script src="../../jquery.js"></script> + <link rel="stylesheet" href="../../../external/qunit.css"> + <script src="../../../external/qunit.js"></script> + <script src="../../jquery.simulate.js"></script> + <script src="../testsuite.js"></script> + <script> + TestHelpers.loadResources({ + css: [ "ui.core", "ui.slider" ], + js: [ + "ui/jquery.ui.core.js", + "ui/jquery.ui.widget.js", + "ui/jquery.ui.mouse.js", + "ui/jquery.ui.slider.js" + ] + }); + </script> + + <script src="slider_common.js"></script> + <script src="slider_core.js"></script> + <script src="slider_events.js"></script> + <script src="slider_methods.js"></script> + <script src="slider_options.js"></script> + + <script src="../swarminject.js"></script> +</head> +<body> + +<h1 id="qunit-header">jQuery UI Slider Test Suite</h1> +<h2 id="qunit-banner"></h2> +<div id="qunit-testrunner-toolbar"></div> +<h2 id="qunit-userAgent"></h2> +<ol id="qunit-tests"></ol> +<div id="qunit-fixture"> + +<div id="slider1"></div> +<div id="slider3" style="position: relative; margin: 40px; width: 217px; height: 28px;"> + <div class="ui-slider-handle" style="position: absolute; height: 21px; left: 0px; bottom: 0px; width: 17px;"></div> +</div> + +</div> +</body> +</html> diff --git a/apps/it/static/js/ui/tests/unit/slider/slider_common.js b/apps/it/static/js/ui/tests/unit/slider/slider_common.js new file mode 100644 index 0000000..6d7278d --- /dev/null +++ b/apps/it/static/js/ui/tests/unit/slider/slider_common.js @@ -0,0 +1,23 @@ +TestHelpers.commonWidgetTests( "slider", { + defaults: { + animate: false, + cancel: "input,textarea,button,select,option", + delay: 0, + disabled: false, + distance: 0, + max: 100, + min: 0, + orientation: "horizontal", + range: false, + step: 1, + value: 0, + values: null, + + // callbacks + create: null, + change: null, + slide: null, + start: null, + stop: null + } +}); diff --git a/apps/it/static/js/ui/tests/unit/slider/slider_core.js b/apps/it/static/js/ui/tests/unit/slider/slider_core.js new file mode 100644 index 0000000..3649bde --- /dev/null +++ b/apps/it/static/js/ui/tests/unit/slider/slider_core.js @@ -0,0 +1,297 @@ +(function( $ ) { +// +// Slider Test Helper Functions +// + +var element, options; + +function handle() { + return element.find( ".ui-slider-handle" ); +} + +// Slider Tests +module( "slider: core" ); + +test( "keydown HOME on handle sets value to min", function() { + expect( 2 ); + element = $( "<div></div>" ); + options = { + max: 5, + min: -5, + orientation: "horizontal", + step: 1 + }; + element.slider( options ); + + element.slider( "value", 0 ); + + handle().simulate( "keydown", { keyCode: $.ui.keyCode.HOME } ); + equal(element.slider( "value" ), options.min ); + + element.slider( "destroy" ); + + element = $( "<div></div>" ); + options = { + max: 5, + min: -5, + orientation: "vertical", + step: 1 + }; + element.slider( options ); + + element.slider( "value", 0 ); + + handle().simulate( "keydown", { keyCode: $.ui.keyCode.HOME } ); + equal(element.slider( "value" ), options.min) ; + + element.slider( "destroy" ); +}); + +test( "keydown END on handle sets value to max", function() { + expect( 2 ); + element = $( "<div></div>" ); + options = { + max: 5, + min: -5, + orientation: "horizontal", + step: 1 + }; + element.slider( options ); + + element.slider( "value", 0 ); + + handle().simulate( "keydown", { keyCode: $.ui.keyCode.END } ); + equal(element.slider( "value" ), options.max) ; + + element.slider( "destroy" ); + + element = $( "<div></div>" ); + options = { + max: 5, + min: -5, + orientation: "vertical", + step: 1 + }; + element.slider( options ); + + element.slider( "value", 0 ); + + handle().simulate( "keydown", { keyCode: $.ui.keyCode.END } ); + equal(element.slider( "value" ), options.max ); + + element.slider( "destroy" ); +}); + +test( "keydown PAGE_UP on handle increases value by 1/5 range, not greater than max", function() { + expect( 4 ); + $.each( [ "horizontal", "vertical" ], function( i, orientation ) { + element = $( "<div></div>" ); + options = { + max: 100, + min: 0, + orientation: orientation, + step: 1 + }; + element.slider( options ); + + element.slider( "value", 70); + + handle().simulate( "keydown", { keyCode: $.ui.keyCode.PAGE_UP } ); + equal(element.slider( "value" ), 90); + + handle().simulate( "keydown", { keyCode: $.ui.keyCode.PAGE_UP } ); + equal(element.slider( "value" ), 100); + + element.slider( "destroy" ); + }); +}); + +test( "keydown PAGE_DOWN on handle decreases value by 1/5 range, not less than min", function() { + expect( 4 ); + $.each( [ "horizontal", "vertical" ], function( i, orientation ) { + element = $( "<div></div>" ); + options = { + max: 100, + min: 0, + orientation: orientation, + step: 1 + }; + element.slider( options ); + + element.slider( "value", 30); + + handle().simulate( "keydown", { keyCode: $.ui.keyCode.PAGE_DOWN } ); + equal(element.slider( "value" ), 10); + + handle().simulate( "keydown", { keyCode: $.ui.keyCode.PAGE_DOWN } ); + equal(element.slider( "value" ), 0 ); + + element.slider( "destroy" ); + }); +}); + +test( "keydown UP on handle increases value by step, not greater than max", function() { + expect( 4 ); + element = $( "<div></div>" ); + options = { + max: 5, + min: -5, + orientation: "horizontal", + step: 1 + }; + element.slider(options); + + element.slider( "value", options.max - options.step ); + + handle().simulate( "keydown", { keyCode: $.ui.keyCode.UP } ); + equal(element.slider( "value" ), options.max ); + + handle().simulate( "keydown", { keyCode: $.ui.keyCode.UP } ); + equal(element.slider( "value" ), options.max ); + + element.slider( "destroy" ); + + element = $( "<div></div>" ); + options = { + max: 5, + min: -5, + orientation: "vertical", + step: 1 + }; + element.slider( options ); + + element.slider( "value", options.max - options.step ); + + handle().simulate( "keydown", { keyCode: $.ui.keyCode.UP } ); + equal(element.slider( "value" ), options.max ); + + handle().simulate( "keydown", { keyCode: $.ui.keyCode.UP } ); + equal(element.slider( "value" ), options.max ); + + element.slider( "destroy" ); +}); + +test( "keydown RIGHT on handle increases value by step, not greater than max", function() { + expect( 4 ); + element = $( "<div></div>" ); + options = { + max: 5, + min: -5, + orientation: "horizontal", + step: 1 + }; + element.slider(options); + + element.slider( "value", options.max - options.step ); + + handle().simulate( "keydown", { keyCode: $.ui.keyCode.RIGHT } ); + equal(element.slider( "value" ), options.max); + + handle().simulate( "keydown", { keyCode: $.ui.keyCode.RIGHT } ); + equal(element.slider( "value" ), options.max ); + + element.slider( "destroy" ); + + element = $( "<div></div>" ); + options = { + max: 5, + min: -5, + orientation: "vertical", + step: 1 + }; + element.slider( options ); + + element.slider( "value", options.max - options.step ); + + handle().simulate( "keydown", { keyCode: $.ui.keyCode.RIGHT } ); + equal(element.slider( "value" ), options.max ); + + handle().simulate( "keydown", { keyCode: $.ui.keyCode.RIGHT } ); + equal(element.slider( "value" ), options.max ); + + element.slider( "destroy" ); +}); + +test( "keydown DOWN on handle decreases value by step, not less than min", function() { + expect( 4 ); + element = $( "<div></div>" ); + options = { + max: 5, + min: -5, + orientation: "horizontal", + step: 1 + }; + element.slider( options ); + + element.slider( "value", options.min + options.step ); + + handle().simulate( "keydown", { keyCode: $.ui.keyCode.DOWN } ); + equal(element.slider( "value" ), options.min); + + handle().simulate( "keydown", { keyCode: $.ui.keyCode.DOWN } ); + equal(element.slider( "value" ), options.min ); + + element.slider( "destroy" ); + + element = $( "<div></div>" ); + options = { + max: 5, + min: -5, + orientation: "vertical", + step: 1 + }; + element.slider( options ); + + element.slider( "value", options.min + options.step ); + + handle().simulate( "keydown", { keyCode: $.ui.keyCode.DOWN } ); + equal(element.slider( "value" ), options.min); + + handle().simulate( "keydown", { keyCode: $.ui.keyCode.DOWN } ); + equal(element.slider( "value" ), options.min ); + + element.slider( "destroy" ); +}); + +test( "keydown LEFT on handle decreases value by step, not less than min", function() { + expect( 4 ); + element = $( "<div></div>" ); + options = { + max: 5, + min: -5, + orientation: "horizontal", + step: 1 + }; + element.slider(options); + + element.slider( "value", options.min + options.step ); + + handle().simulate( "keydown", { keyCode: $.ui.keyCode.LEFT } ); + equal(element.slider( "value" ), options.min ); + + handle().simulate( "keydown", { keyCode: $.ui.keyCode.LEFT } ); + equal(element.slider( "value" ), options.min ); + + element.slider( "destroy" ); + + element = $( "<div></div>" ); + options = { + max: 5, + min: -5, + orientation: "vertical", + step: 1 + }; + element.slider( options ); + + element.slider( "value", options.min + options.step ); + + handle().simulate( "keydown", { keyCode: $.ui.keyCode.LEFT } ); + equal(element.slider( "value" ), options.min ); + + handle().simulate( "keydown", { keyCode: $.ui.keyCode.LEFT } ); + equal(element.slider( "value" ), options.min ); + + element.slider( "destroy" ); +}); + +})( jQuery ); diff --git a/apps/it/static/js/ui/tests/unit/slider/slider_events.js b/apps/it/static/js/ui/tests/unit/slider/slider_events.js new file mode 100644 index 0000000..ec93a05 --- /dev/null +++ b/apps/it/static/js/ui/tests/unit/slider/slider_events.js @@ -0,0 +1,152 @@ +(function( $ ) { + +module( "slider: events" ); + +//Specs from http://wiki.jqueryui.com/Slider#specs +//"change callback: triggers when the slider has stopped moving and has a new +// value (even if same as previous value), via mouse(mouseup) or keyboard(keyup) +// or value method/option" +test( "mouse based interaction", function() { + expect( 4 ); + + var element = $( "#slider1" ) + .slider({ + start: function( event ) { + equal( event.originalEvent.type, "mousedown", "start triggered by mousedown" ); + }, + slide: function( event) { + equal( event.originalEvent.type, "mousemove", "slider triggered by mousemove" ); + }, + stop: function( event ) { + equal( event.originalEvent.type, "mouseup", "stop triggered by mouseup" ); + }, + change: function( event ) { + equal( event.originalEvent.type, "mouseup", "change triggered by mouseup" ); + } + }); + + element.find( ".ui-slider-handle" ).eq( 0 ) + .simulate( "drag", { dx: 10, dy: 10 } ); + +}); +test( "keyboard based interaction", function() { + expect( 3 ); + + // Test keyup at end of handle slide (keyboard) + var element = $( "#slider1" ) + .slider({ + start: function( event ) { + equal( event.originalEvent.type, "keydown", "start triggered by keydown" ); + }, + slide: function() { + ok( false, "Slider never triggered by keys" ); + }, + stop: function( event ) { + equal( event.originalEvent.type, "keyup", "stop triggered by keyup" ); + }, + change: function( event ) { + equal( event.originalEvent.type, "keyup", "change triggered by keyup" ); + } + }); + + element.find( ".ui-slider-handle" ).eq( 0 ) + .simulate( "keydown", { keyCode: $.ui.keyCode.LEFT } ) + .simulate( "keypress", { keyCode: $.ui.keyCode.LEFT } ) + .simulate( "keyup", { keyCode: $.ui.keyCode.LEFT } ); + +}); +test( "programmatic event triggers", function() { + expect( 6 ); + + // Test value method + var element = $( "<div></div>" ) + .slider({ + change: function() { + ok( true, "change triggered by value method" ); + } + }) + .slider( "value", 0 ); + + // Test values method + element = $( "<div></div>" ) + .slider({ + values: [ 10, 20 ], + change: function() { + ok( true, "change triggered by values method" ); + } + }) + .slider( "values", [ 80, 90 ] ); + + // Test value option + element = $( "<div></div>" ) + .slider({ + change: function() { + ok( true, "change triggered by value option" ); + } + }) + .slider( "option", "value", 0 ); + + // Test values option + element = $( "<div></div>" ) + .slider({ + values: [ 10, 20 ], + change: function() { + ok( true, "change triggered by values option" ); + } + }) + .slider( "option", "values", [ 80, 90 ] ); + +}); + +test( "mouse based interaction part two: when handles overlap", function() { + expect( 4 ); + + var element = $( "#slider1" ) + .slider({ + values: [ 0, 0, 0 ], + start: function( event, ui ) { + equal( handles.index( ui.handle ), 2, "rightmost handle activated when overlapping at minimum (#3736)" ); + } + }), + handles = element.find( ".ui-slider-handle" ); + handles.eq( 0 ).simulate( "drag", { dx: 10 } ); + element.slider( "destroy" ); + + element = $( "#slider1" ) + .slider({ + values: [ 10, 10, 10 ], + max: 10, + start: function( event, ui ) { + equal( handles.index( ui.handle ), 0, "leftmost handle activated when overlapping at maximum" ); + } + }), + handles = element.find( ".ui-slider-handle" ); + handles.eq( 0 ).simulate( "drag", { dx: -10 } ); + element.slider( "destroy" ); + + element = $( "#slider1" ) + .slider({ + values: [ 19, 20 ] + }), + handles = element.find( ".ui-slider-handle" ); + handles.eq( 0 ).simulate( "drag", { dx: 10 } ); + element.one( "slidestart", function( event, ui ) { + equal( handles.index( ui.handle ), 0, "left handle activated if left was moved last" ); + }); + handles.eq( 0 ).simulate( "drag", { dx: 10 } ); + element.slider( "destroy" ); + + element = $( "#slider1" ) + .slider({ + values: [ 19, 20 ] + }), + handles = element.find( ".ui-slider-handle" ); + handles.eq( 1 ).simulate( "drag", { dx: -10 } ); + element.one( "slidestart", function( event, ui ) { + equal( handles.index( ui.handle ), 1, "right handle activated if right was moved last (#3467)" ); + }); + handles.eq( 0 ).simulate( "drag", { dx: 10 } ); + +}); + +})( jQuery ); diff --git a/apps/it/static/js/ui/tests/unit/slider/slider_methods.js b/apps/it/static/js/ui/tests/unit/slider/slider_methods.js new file mode 100644 index 0000000..e13fbd5 --- /dev/null +++ b/apps/it/static/js/ui/tests/unit/slider/slider_methods.js @@ -0,0 +1,96 @@ +(function( $ ) { + +module( "slider: methods" ); + +test( "init", function() { + expect(5); + + $( "<div></div>" ).appendTo( "body" ).slider().remove(); + ok( true, ".slider() called on element" ); + + $( [] ).slider().remove(); + ok( true, ".slider() called on empty collection" ); + + $( "<div></div>" ).slider().remove(); + ok( true, ".slider() called on disconnected DOMElement" ); + + var element = $( "<div></div>" ).slider(); + element.slider( "option", "foo" ); + element.remove(); + ok( true, "arbitrary option getter after init" ); + + $( "<div></div>" ).slider().slider( "option", "foo", "bar" ).remove(); + ok( true, "arbitrary option setter after init" ); +}); + +test( "destroy", function() { + expect( 1 ); + domEqual( "#slider1", function() { + $( "#slider1" ).slider().slider( "destroy" ); + }); +}); + +test( "enable", function() { + expect( 5 ); + var element, + expected = $( "<div></div>" ).slider(), + actual = expected.slider( "enable" ); + equal(actual, expected, "enable is chainable" ); + + element = $( "<div></div>" ).slider({ disabled: true }); + ok( element.hasClass( "ui-state-disabled" ), "slider has ui-state-disabled class before enable method call" ); + ok( element.hasClass( "ui-slider-disabled" ), "slider has ui-slider-disabled class before enable method call" ); + element.slider( "enable" ); + ok( !element.hasClass( "ui-state-disabled" ), "slider does not have ui-state-disabled class after enable method call" ); + ok( !element.hasClass( "ui-slider-disabled" ), "slider does not have ui-slider-disabled class after enable method call" ); +}); + +test( "disable", function() { + expect( 5 ); + var element, + expected = $( "<div></div>" ).slider(), + actual = expected.slider( "disable" ); + equal(actual, expected, "disable is chainable" ); + + element = $( "<div></div>" ).slider({ disabled: false }); + ok( !element.hasClass( "ui-state-disabled" ), "slider does not have ui-state-disabled class before disabled method call" ); + ok( !element.hasClass( "ui-slider-disabled" ), "slider does not have ui-slider-disabled class before disable method call" ); + element.slider( "disable" ); + ok( element.hasClass( "ui-state-disabled" ), "slider has ui-state-disabled class after disable method call" ); + ok( element.hasClass( "ui-slider-disabled" ), "slider has ui-slider-disabled class after disable method call" ); +}); + +test( "value", function() { + expect( 17 ); + $( [ false, "min", "max" ] ).each(function() { + var element = $( "<div></div>" ).slider({ + range: this, + value: 5 + }); + equal( element.slider( "value" ), 5, "range: " + this + " slider method get" ); + equal( element.slider( "value", 10), element, "value method is chainable" ); + equal( element.slider( "value" ), 10, "range: " + this + " slider method set" ); + element.remove(); + }); + var element = $( "<div></div>" ).slider({ + min: -1, value: 0, max: 1 + }); + // min with value option vs value method + element.slider( "option", "value", -2 ); + equal( element.slider( "option", "value" ), -2, "value option does not respect min" ); + equal( element.slider( "value" ), -1, "value method get respects min" ); + equal( element.slider( "value", -2 ), element, "value method is chainable" ); + equal( element.slider( "option", "value" ), -1, "value method set respects min" ); + // max with value option vs value method + element.slider( "option", "value", 2); + equal( element.slider( "option", "value" ), 2, "value option does not respect max" ); + equal( element.slider( "value" ), 1, "value method get respects max" ); + equal( element.slider( "value", 2 ), element, "value method is chainable" ); + equal( element.slider( "option", "value" ), 1, "value method set respects max" ); +}); + +//test( "values", function() { +// ok(false, "missing test - untested code is broken code." ); +//}); + +})( jQuery ); diff --git a/apps/it/static/js/ui/tests/unit/slider/slider_options.js b/apps/it/static/js/ui/tests/unit/slider/slider_options.js new file mode 100644 index 0000000..bd62f5d --- /dev/null +++ b/apps/it/static/js/ui/tests/unit/slider/slider_options.js @@ -0,0 +1,317 @@ +(function( $ ) { + +var element, options; + +function handle() { + return element.find( ".ui-slider-handle" ); +} + +module( "slider: options" ); + +test( "disabled", function(){ + expect( 8 ); + var count = 0; + + element = $( "#slider1" ).slider(); + element.bind( "slidestart", function() { + count++; + }); + + // enabled + ok( !element.hasClass( "ui-slider-disabled" ), "no disabled class" ); + equal( element.slider( "option", "disabled" ), false , "is not disabled" ); + + handle().simulate( "drag", { dx: 10 } ); + equal( count, 1, "slider moved" ); + + handle().simulate( "keydown", { keyCode: $.ui.keyCode.RIGHT } ); + equal( count, 2, "slider moved" ); + + // disabled + element.slider( "option", "disabled", true ); + ok( element.hasClass( "ui-slider-disabled" ), "has disabled class" ); + equal( element.slider( "option", "disabled" ), true, "is disabled" ); + + handle().simulate( "drag", { dx: 10 } ); + equal( count, 2, "slider did not move" ); + + handle().simulate( "keydown", { keyCode: $.ui.keyCode.RIGHT } ); + equal( count, 2, "slider did not move" ); +}); + +test( "max", function() { + expect( 2 ); + element = $( "<div></div>" ); + + options = { + max: 37, + min: 6, + orientation: "horizontal", + step: 1, + value: 50 + }; + + element.slider( options ); + ok(element.slider( "option", "value" ) === options.value, "value option is not contained by max" ); + ok(element.slider( "value" ) === options.max, "value method is contained by max" ); + element.slider( "destroy" ); + +}); + +test( "min", function() { + expect( 2 ); + element = $( "<div></div>" ); + + options = { + max: 37, + min: 6, + orientation: "vertical", + step: 1, + value: 2 + }; + + element.slider( options ); + ok( element.slider( "option", "value" ) === options.value, "value option is not contained by min" ); + ok( element.slider( "value" ) === options.min, "value method is contained by min" ); + element.slider( "destroy" ); + +}); + +test( "orientation", function() { + expect( 6 ); + element = $( "#slider1" ); + + options = { + max: 2, + min: -2, + orientation: "vertical", + value: 1 + }; + + var percentVal = ( options.value - options.min ) / ( options.max - options.min ) * 100; + + element.slider( options ).slider( "option", "orientation", "horizontal" ); + ok( element.is( ".ui-slider-horizontal" ), "horizontal slider has class .ui-slider-horizontal" ); + ok( !element.is( ".ui-slider-vertical" ), "horizontal slider does not have class .ui-slider-vertical" ); + equal( handle()[0].style.left, percentVal + "%", "horizontal slider handle is positioned with left: %" ); + + element.slider( "destroy" ) ; + + options = { + max: 2, + min: -2, + orientation: "horizontal", + value: -1 + }; + + percentVal = ( options.value - options.min ) / ( options.max - options.min ) * 100; + + element.slider( options ).slider( "option", "orientation", "vertical" ); + ok( element.is( ".ui-slider-vertical" ), "vertical slider has class .ui-slider-vertical" ); + ok( !element.is( ".ui-slider-horizontal" ), "vertical slider does not have class .ui-slider-horizontal" ); + equal( handle()[0].style.bottom, percentVal + "%", "vertical slider handle is positioned with bottom: %" ); + + element.slider( "destroy" ); + +}); + +//spec: http://wiki.jqueryui.com/Slider#specs +// value option/method: the value option is not restricted by min/max/step. +// What is returned by the value method is restricted by min (>=), max (<=), and step (even multiple) +test( "step", function() { + expect( 9 ); + element = $( "<div></div>" ).slider({ + min: 0, + value: 0, + step: 10, + max: 100 + }); + equal( element.slider( "value" ), 0 ); + + element.slider( "value", 1 ); + equal( element.slider( "value" ), 0 ); + + element.slider( "value", 9 ); + equal( element.slider( "value" ), 10 ); + + element.slider( "value", 11 ); + equal( element.slider( "value" ), 10 ); + + element.slider( "value", 19 ); + equal( element.slider( "value" ), 20 ); + + element = $( "<div></div>" ).slider({ + min: 0, + value: 0, + step: 20, + max: 100 + }); + element.slider( "value", 0 ); + + element.slider( "option", "value", 1 ); + equal( element.slider( "value" ), 0 ); + + element.slider( "option", "value", 9 ); + equal( element.slider( "value" ), 0 ); + + element.slider( "option", "value", 11 ); + equal( element.slider( "value" ), 20 ); + + element.slider( "option", "value", 19 ); + equal( element.slider( "value" ), 20 ); + + element.slider( "destroy" ); +}); + +//test( "value", function() { +// ok(false, "missing test - untested code is broken code." ); +//}); + +test( "values", function() { + expect( 2 ); + + // testing multiple ranges on the same page, the object reference to the values + // property is preserved via multiple range elements, so updating options.values + // of 1 slider updates options.values of all the others + var ranges = $([ + document.createElement( "div" ), + document.createElement( "div" ) + ]).slider({ + range: true, + values: [ 25, 75 ] + }); + + notStrictEqual( + ranges.eq( 0 ).data( "ui-slider" ).options.values, + ranges.eq( 1 ).data( "ui-slider" ).options.values, + "multiple range sliders should not have a reference to the same options.values array" + ); + + ranges.eq( 0 ).slider( "values", 0, 10 ); + + notEqual( + ranges.eq( 0 ).slider( "values", 0 ), + ranges.eq( 1 ).slider( "values", 0 ), + "the values for multiple sliders should be different" + ); +}); + +test( "range", function() { + expect( 33 ); + var range; + + // min + element = $( "<div></div>" ).slider({ + range: "min", + min: 1, + max: 10, + step: 1 + }); + + equal( element.find( ".ui-slider-handle" ).length, 1, "range min, one handle" ); + equal( element.find( ".ui-slider-range-min" ).length, 1, "range min" ); + element.slider( "destroy" ); + + // max + element = $( "<div></div>" ).slider({ + range: "max", + min: 1, + max: 10, + step: 1 + }); + + equal( element.find( ".ui-slider-handle" ).length, 1, "range max, one handle" ); + equal( element.find( ".ui-slider-range-max" ).length, 1, "range max" ); + element.slider( "destroy" ); + + // true + element = $( "<div></div>" ).slider({ + range: true, + min: 1, + max: 10, + step: 1 + }); + + range = element.find( ".ui-slider-range" ); + equal( element.find( ".ui-slider-handle" ).length, 2, "range true, two handles" ); + ok( !range.is( ".ui-slider-range-min" ), "range true" ); + ok( !range.is( ".ui-slider-range-max" ), "range true" ); + element.slider( "destroy" ); + + // Change range from min to max + element = $( "<div></div>" ).slider({ + range: "min", + min: 1, + max: 10, + step: 1 + }).slider( "option", "range", "max" ); + + equal( element.find( ".ui-slider-handle" ).length, 1, "range switch from min to max, one handle" ); + equal( element.find( ".ui-slider-range-min" ).length, 0, "range switch from min to max" ); + equal( element.find( ".ui-slider-range-max" ).length, 1, "range switch from min to max" ); + element.slider( "destroy" ); + + // Change range from max to min + element = $( "<div></div>" ).slider({ + range: "max", + min: 1, + max: 10, + step: 1 + }).slider( "option", "range", "min" ); + + equal( element.find( ".ui-slider-handle" ).length, 1, "range switch from max to min, one handle" ); + equal( element.find( ".ui-slider-range-max" ).length, 0, "range switch from max to min" ); + equal( element.find( ".ui-slider-range-min" ).length, 1, "range switch from max to min" ); + element.slider( "destroy" ); + + // Change range from max to true + element = $( "<div></div>" ).slider({ + range: "max", + min: 1, + max: 10, + step: 1 + }).slider( "option", "range", true ); + + equal( element.find( ".ui-slider-handle" ).length, 2, "range switch from max to true, two handles" ); + equal( element.find( ".ui-slider-range-max" ).length, 0, "range switch from max to true" ); + equal( element.find( ".ui-slider-range-min" ).length, 0, "range switch from max to true" ); + equal( element.slider( "option", "value" ), 0 , "option value" ); + equal( element.slider( "value" ), 1 , "value" ); + deepEqual( element.slider( "option", "values" ), [1, 1], "option values" ); + deepEqual( element.slider( "values" ), [1, 1], "values" ); + element.slider( "destroy" ); + + // Change range from true to min + element = $( "<div></div>" ).slider({ + range: true, + min: 1, + max: 10, + step: 1 + }).slider( "option", "range", "min" ); + + equal( element.find( ".ui-slider-handle" ).length, 1, "range switch from true to min, one handle" ); + equal( element.find( ".ui-slider-range-max" ).length, 0, "range switch from true to min" ); + equal( element.find( ".ui-slider-range-min" ).length, 1, "range switch from true to min" ); + equal( element.slider( "option", "value" ), 1, "value" ); + equal( element.slider( "value" ), 1 , "value" ); + equal( element.slider( "option", "values" ), null, "values" ); + deepEqual( element.slider( "values" ), [] , "values" ); + element.slider( "destroy" ); + + // Change range from true to false + element = $( "<div></div>" ).slider({ + range: true, + min: 1, + max: 10, + step: 1 + }).slider( "option", "range", false ); + equal( element.find( ".ui-slider-handle" ).length, 2, "range switch from true to false, both handles remain" ); + equal( element.find( ".ui-slider-range" ).length, 0, "range switch from true to false" ); + equal( element.slider( "option", "value" ), 0 , "option value" ); + equal( element.slider( "value" ), 1 , "value" ); + deepEqual( element.slider( "option", "values" ), [1, 1], "option values" ); + deepEqual( element.slider( "values" ), [1, 1], "values" ); + element.slider( "destroy" ); +}); + +})( jQuery ); diff --git a/apps/it/static/js/ui/tests/unit/sortable/all.html b/apps/it/static/js/ui/tests/unit/sortable/all.html new file mode 100644 index 0000000..03253b9 --- /dev/null +++ b/apps/it/static/js/ui/tests/unit/sortable/all.html @@ -0,0 +1,30 @@ +<!doctype html> +<html lang="en"> +<head> + <meta charset="utf-8"> + <title>jQuery UI Sortable Test Suite</title> + + <script src="../../../jquery-1.10.2.js"></script> + + <link rel="stylesheet" href="../../../external/qunit.css"> + <link rel="stylesheet" href="../qunit-composite.css"> + <script src="../../../external/qunit.js"></script> + <script src="../qunit-composite.js"></script> + <script src="../subsuite.js"></script> + + <script> + testAllVersions( "sortable" ); + </script> +</head> +<body> + +<h1 id="qunit-header">jQuery UI Sortable Test Suite</h1> +<h2 id="qunit-banner"></h2> +<div id="qunit-testrunner-toolbar"></div> +<h2 id="qunit-userAgent"></h2> +<ol id="qunit-tests"></ol> +<div id="qunit-fixture"> + +</div> +</body> +</html> diff --git a/apps/it/static/js/ui/tests/unit/sortable/sortable.html b/apps/it/static/js/ui/tests/unit/sortable/sortable.html new file mode 100644 index 0000000..8e0bac5 --- /dev/null +++ b/apps/it/static/js/ui/tests/unit/sortable/sortable.html @@ -0,0 +1,99 @@ +<!doctype html> +<html lang="en"> +<head> + <meta charset="utf-8"> + <title>jQuery UI Sortable Test Suite</title> + + <script src="../../jquery.js"></script> + <link rel="stylesheet" href="../../../external/qunit.css"> + <script src="../../../external/qunit.js"></script> + <script src="../../jquery.simulate.js"></script> + <script src="../testsuite.js"></script> + <script> + TestHelpers.loadResources({ + css: [ "ui.core" ], + js: [ + "ui/jquery.ui.core.js", + "ui/jquery.ui.widget.js", + "ui/jquery.ui.mouse.js", + "ui/jquery.ui.sortable.js" + ] + }); + </script> + + <script src="sortable_common.js"></script> + <script src="sortable_core.js"></script> + <script src="sortable_events.js"></script> + <script src="sortable_methods.js"></script> + <script src="sortable_options.js"></script> + <script src="sortable_test_helpers.js"></script> + + <script src="../swarminject.js"></script> + <style> + #sortable, #sortable2 { + position:relative; + top:0; + left:0; + padding: 0; + margin: 1px; + border-width: 0; + } + #sortable li{ + padding: 0; + margin: 0; + border-width: 0; + height:19px; + } + #sortable-table { + width: 100%; + } + </style> +</head> +<body> + +<h1 id="qunit-header">jQuery UI Sortable Test Suite</h1> +<h2 id="qunit-banner"></h2> +<div id="qunit-testrunner-toolbar"></div> +<h2 id="qunit-userAgent"></h2> +<ol id="qunit-tests"></ol> +<div id="qunit-fixture"> + +<ul id="sortable"> + <li>Item 1</li> + <li>Item 2</li> + <li>Item 3</li> + <li>Item 4</li> + <li>Item 5</li> +</ul> + +<table id="sortable-table"> + <tbody> + <tr> + <td>1</td> + <td>2</td> + </tr> + <tr> + <td>3</td> + <td>4</td> + </tr> + <tr> + <td>5</td> + <td>6</td> + </tr> + <tr> + <td>7</td> + <td>8</td> + </tr> + </tbody> +</table> + +<div id="sortable-images"> + <img src="../images/jqueryui_32x32.png" alt=""> + <img src="../images/jqueryui_32x32.png" alt=""> + <img src="../images/jqueryui_32x32.png" alt=""> + <img src="../images/jqueryui_32x32.png" alt=""> +</div> + +</div> +</body> +</html> diff --git a/apps/it/static/js/ui/tests/unit/sortable/sortable_common.js b/apps/it/static/js/ui/tests/unit/sortable/sortable_common.js new file mode 100644 index 0000000..86850a6 --- /dev/null +++ b/apps/it/static/js/ui/tests/unit/sortable/sortable_common.js @@ -0,0 +1,45 @@ +TestHelpers.commonWidgetTests( "sortable", { + defaults: { + appendTo: "parent", + axis: false, + cancel: "input,textarea,button,select,option", + connectWith: false, + containment: false, + cursor: "auto", + cursorAt: false, + delay: 0, + disabled: false, + distance: 1, + dropOnEmpty: true, + forcePlaceholderSize: false, + forceHelperSize: false, + grid: false, + handle: false, + helper: "original", + items: "> *", + opacity: false, + placeholder: false, + revert: false, + scroll: true, + scrollSensitivity: 20, + scrollSpeed: 20, + scope: "default", + tolerance: "intersect", + zIndex: 1000, + + // callbacks + activate: null, + beforeStop: null, + change: null, + create: null, + deactivate: null, + out: null, + over: null, + receive: null, + remove: null, + sort: null, + start: null, + stop: null, + update: null + } +}); diff --git a/apps/it/static/js/ui/tests/unit/sortable/sortable_core.js b/apps/it/static/js/ui/tests/unit/sortable/sortable_core.js new file mode 100644 index 0000000..211f8ac --- /dev/null +++ b/apps/it/static/js/ui/tests/unit/sortable/sortable_core.js @@ -0,0 +1,3 @@ +/* + * sortable_core.js + */
\ No newline at end of file diff --git a/apps/it/static/js/ui/tests/unit/sortable/sortable_events.js b/apps/it/static/js/ui/tests/unit/sortable/sortable_events.js new file mode 100644 index 0000000..46a493b --- /dev/null +++ b/apps/it/static/js/ui/tests/unit/sortable/sortable_events.js @@ -0,0 +1,275 @@ +/* + * sortable_events.js + */ +(function($) { + +module("sortable: events"); + +test("start", function() { + expect( 7 ); + + var hash; + $("#sortable").sortable({ + start: function( e, ui ) { + hash = ui; + } + }).find("li:eq(0)").simulate( "drag", { + dy: 10 + }); + + ok(hash, "start event triggered"); + ok(hash.helper, "UI hash includes: helper"); + ok(hash.placeholder, "UI hash includes: placeholder"); + ok(hash.item, "UI hash includes: item"); + ok(!hash.sender, "UI hash does not include: sender"); + + // todo: see if these events should actually have sane values in them + ok("position" in hash, "UI hash includes: position"); + ok("offset" in hash, "UI hash includes: offset"); + + +}); + +test("sort", function() { + expect( 7 ); + + var hash; + $("#sortable").sortable({ + sort: function( e, ui ) { + hash = ui; + } + }).find("li:eq(0)").simulate( "drag", { + dy: 10 + }); + + ok(hash, "sort event triggered"); + ok(hash.helper, "UI hash includes: helper"); + ok(hash.placeholder, "UI hash includes: placeholder"); + ok(hash.position && ("top" in hash.position && "left" in hash.position), "UI hash includes: position"); + ok(hash.offset && (hash.offset.top && hash.offset.left), "UI hash includes: offset"); + ok(hash.item, "UI hash includes: item"); + ok(!hash.sender, "UI hash does not include: sender"); + +}); + +test("change", function() { + expect( 8 ); + + var hash; + $("#sortable").sortable({ + change: function( e, ui ) { + hash = ui; + } + }).find("li:eq(0)").simulate( "drag", { + dx: 1, + dy: 1 + }); + + ok(!hash, "1px drag, change event should not be triggered"); + + $("#sortable").sortable({ + change: function( e, ui ) { + hash = ui; + } + }).find("li:eq(0)").simulate( "drag", { + dy: 22 + }); + + ok(hash, "change event triggered"); + ok(hash.helper, "UI hash includes: helper"); + ok(hash.placeholder, "UI hash includes: placeholder"); + ok(hash.position && ("top" in hash.position && "left" in hash.position), "UI hash includes: position"); + ok(hash.offset && (hash.offset.top && hash.offset.left), "UI hash includes: offset"); + ok(hash.item, "UI hash includes: item"); + ok(!hash.sender, "UI hash does not include: sender"); + +}); + +test("beforeStop", function() { + expect( 7 ); + + var hash; + $("#sortable").sortable({ + beforeStop: function( e, ui ) { + hash = ui; + } + }).find("li:eq(0)").simulate( "drag", { + dy: 20 + }); + + ok(hash, "beforeStop event triggered"); + ok(hash.helper, "UI hash includes: helper"); + ok(hash.placeholder, "UI hash includes: placeholder"); + ok(hash.position && ("top" in hash.position && "left" in hash.position), "UI hash includes: position"); + ok(hash.offset && (hash.offset.top && hash.offset.left), "UI hash includes: offset"); + ok(hash.item, "UI hash includes: item"); + ok(!hash.sender, "UI hash does not include: sender"); + +}); + +test("stop", function() { + expect( 7 ); + + var hash; + $("#sortable").sortable({ + stop: function( e, ui ) { + hash = ui; + } + }).find("li:eq(0)").simulate( "drag", { + dy: 20 + }); + + ok(hash, "stop event triggered"); + ok(!hash.helper, "UI should not include: helper"); + ok(hash.placeholder, "UI hash includes: placeholder"); + ok(hash.position && ("top" in hash.position && "left" in hash.position), "UI hash includes: position"); + ok(hash.offset && (hash.offset.top && hash.offset.left), "UI hash includes: offset"); + ok(hash.item, "UI hash includes: item"); + ok(!hash.sender, "UI hash does not include: sender"); + +}); + +test("update", function() { + expect( 8 ); + + var hash; + $("#sortable").sortable({ + update: function( e, ui ) { + hash = ui; + } + }).find("li:eq(0)").simulate( "drag", { + dx: 1, + dy: 1 + }); + + ok(!hash, "1px drag, update event should not be triggered"); + + $("#sortable").sortable({ + update: function( e, ui ) { + hash = ui; + } + }).find("li:eq(0)").simulate( "drag", { + dy: 22 + }); + + ok(hash, "update event triggered"); + ok(!hash.helper, "UI hash should not include: helper"); + ok(hash.placeholder, "UI hash includes: placeholder"); + ok(hash.position && ("top" in hash.position && "left" in hash.position), "UI hash includes: position"); + ok(hash.offset && (hash.offset.top && hash.offset.left), "UI hash includes: offset"); + ok(hash.item, "UI hash includes: item"); + ok(!hash.sender, "UI hash does not include: sender"); + +}); + +test("#3019: Stop fires too early", function() { + expect(2); + + var helper = null, + el = $("#sortable").sortable({ + stop: function(event, ui) { + helper = ui.helper; + } + }); + + TestHelpers.sortable.sort($("li", el)[0], 0, 44, 2, "Dragging the sortable"); + equal(helper, null, "helper should be false"); + +}); + +test("#4752: link event firing on sortable with connect list", function () { + expect( 10 ); + + var fired = {}, + hasFired = function (type) { return (type in fired) && (true === fired[type]); }; + + $("#sortable").clone().attr("id", "sortable2").insertAfter("#sortable"); + + $("#qunit-fixture ul").sortable({ + connectWith: "#qunit-fixture ul", + change: function () { + fired.change = true; + }, + receive: function () { + fired.receive = true; + }, + remove: function () { + fired.remove = true; + } + }); + + $("#qunit-fixture ul").bind("click.ui-sortable-test", function () { + fired.click = true; + }); + + $("#sortable li:eq(0)").simulate("click"); + ok(!hasFired("change"), "Click only, change event should not have fired"); + ok(hasFired("click"), "Click event should have fired"); + + // Drag an item within the first list + fired = {}; + $("#sortable li:eq(0)").simulate("drag", { dx: 0, dy: 40 }); + ok(hasFired("change"), "40px drag, change event should have fired"); + ok(!hasFired("receive"), "Receive event should not have fired"); + ok(!hasFired("remove"), "Remove event should not have fired"); + ok(!hasFired("click"), "Click event should not have fired"); + + // Drag an item from the first list to the second, connected list + fired = {}; + $("#sortable li:eq(0)").simulate("drag", { dx: 0, dy: 150 }); + ok(hasFired("change"), "150px drag, change event should have fired"); + ok(hasFired("receive"), "Receive event should have fired"); + ok(hasFired("remove"), "Remove event should have fired"); + ok(!hasFired("click"), "Click event should not have fired"); +}); + +/* +test("receive", function() { + ok(false, "missing test - untested code is broken code."); +}); + +test("remove", function() { + ok(false, "missing test - untested code is broken code."); +}); +*/ + +test( "over", function() { + expect( 8 ); + + var hash, + overCount = 0; + + $( "#sortable" ).sortable({ + over: function( e, ui ) { + hash = ui; + overCount++; + } + }).find( "li:eq(0)" ).simulate( "drag", { + dy: 20 + }); + + ok( hash, "stop event triggered" ); + ok( hash.helper, "UI should not include: helper" ); + ok( hash.placeholder, "UI hash includes: placeholder" ); + ok( hash.position && ( "top" in hash.position && "left" in hash.position ), "UI hash includes: position" ); + ok( hash.offset && ( hash.offset.top && hash.offset.left ), "UI hash includes: offset" ); + ok( hash.item, "UI hash includes: item" ); + ok( hash.sender, "UI hash does not include: sender" ); + equal( overCount, 1, "over fires only once" ); +}); + +/* +test("out", function() { + ok(false, "missing test - untested code is broken code."); +}); + +test("activate", function() { + ok(false, "missing test - untested code is broken code."); +}); + +test("deactivate", function() { + ok(false, "missing test - untested code is broken code."); +}); +*/ + +})(jQuery); diff --git a/apps/it/static/js/ui/tests/unit/sortable/sortable_methods.js b/apps/it/static/js/ui/tests/unit/sortable/sortable_methods.js new file mode 100644 index 0000000..07a7bc7 --- /dev/null +++ b/apps/it/static/js/ui/tests/unit/sortable/sortable_methods.js @@ -0,0 +1,92 @@ +/* + * sortable_methods.js + */ +(function($) { + +module("sortable: methods"); + +test("init", function() { + expect(5); + + $("<div></div>").appendTo("body").sortable().remove(); + ok(true, ".sortable() called on element"); + + $([]).sortable(); + ok(true, ".sortable() called on empty collection"); + + $("<div></div>").sortable(); + ok(true, ".sortable() called on disconnected DOMElement"); + + $("<div></div>").sortable().sortable("option", "foo"); + ok(true, "arbitrary option getter after init"); + + $("<div></div>").sortable().sortable("option", "foo", "bar"); + ok(true, "arbitrary option setter after init"); +}); + +test("destroy", function() { + expect(4); + $("<div></div>").appendTo("body").sortable().sortable("destroy").remove(); + ok(true, ".sortable('destroy') called on element"); + + $([]).sortable().sortable("destroy"); + ok(true, ".sortable('destroy') called on empty collection"); + + $("<div></div>").sortable().sortable("destroy"); + ok(true, ".sortable('destroy') called on disconnected DOMElement"); + + var expected = $("<div></div>").sortable(), + actual = expected.sortable("destroy"); + equal(actual, expected, "destroy is chainable"); +}); + +test("enable", function() { + expect(5); + + var el, actual, expected; + + el = $("#sortable").sortable({ disabled: true }); + + TestHelpers.sortable.sort($("li", el)[0], 0, 44, 0, ".sortable({ disabled: true })"); + + el.sortable("enable"); + equal(el.sortable("option", "disabled"), false, "disabled option getter"); + + el.sortable("destroy"); + el.sortable({ disabled: true }); + el.sortable("option", "disabled", false); + equal(el.sortable("option", "disabled"), false, "disabled option setter"); + + TestHelpers.sortable.sort($("li", el)[0], 0, 44, 2, ".sortable('option', 'disabled', false)"); + + expected = $("<div></div>").sortable(), + actual = expected.sortable("enable"); + equal(actual, expected, "enable is chainable"); +}); + +test("disable", function() { + expect(7); + + var el, actual, expected; + + el = $("#sortable").sortable({ disabled: false }); + TestHelpers.sortable.sort($("li", el)[0], 0, 44, 2, ".sortable({ disabled: false })"); + + el.sortable("disable"); + TestHelpers.sortable.sort($("li", el)[0], 0, 44, 0, "disabled.sortable getter"); + + el.sortable("destroy"); + + el.sortable({ disabled: false }); + TestHelpers.sortable.sort($("li", el)[0], 0, 44, 2, ".sortable({ disabled: false })"); + el.sortable("option", "disabled", true); + equal(el.sortable("option", "disabled"), true, "disabled option setter"); + ok(el.sortable("widget").is(":not(.ui-state-disabled)"), "sortable element does not get ui-state-disabled since it's an interaction"); + TestHelpers.sortable.sort($("li", el)[0], 0, 44, 0, ".sortable('option', 'disabled', true)"); + + expected = $("<div></div>").sortable(), + actual = expected.sortable("disable"); + equal(actual, expected, "disable is chainable"); +}); + +})(jQuery); diff --git a/apps/it/static/js/ui/tests/unit/sortable/sortable_options.js b/apps/it/static/js/ui/tests/unit/sortable/sortable_options.js new file mode 100644 index 0000000..f2beb4d --- /dev/null +++ b/apps/it/static/js/ui/tests/unit/sortable/sortable_options.js @@ -0,0 +1,460 @@ +/* + * sortable_options.js + */ +(function($) { + +module("sortable: options"); + +/* +test("{ appendTo: 'parent' }, default", function() { + ok(false, "missing test - untested code is broken code."); +}); + +test("{ appendTo: Selector }", function() { + ok(false, "missing test - untested code is broken code."); +}); +*/ + +test( "{ axis: false }, default", function() { + expect( 2 ); + + var offsetAfter, + element = $( "#sortable" ).sortable({ + axis: false, + change: function() { + offsetAfter = item.offset(); + notEqual( offsetAfter.left, offsetBefore.left, "x axis not constrained when axis: false" ); + notEqual( offsetAfter.top, offsetBefore.top, "y axis not constrained when axis: false" ); + } + }), + item = element.find( "li" ).eq( 0 ), + offsetBefore = item.offset(); + + item.simulate( "drag", { + dx: 50, + dy: 25, + moves: 1 + }); +}); + +test( "{ axis: 'x' }", function() { + expect( 2 ); + + var offsetAfter, + element = $( "#sortable" ).sortable({ + axis: "x", + change: function() { + offsetAfter = item.offset(); + notEqual( offsetAfter.left, offsetBefore.left, "x axis not constrained when axis: x" ); + equal( offsetAfter.top, offsetBefore.top, "y axis constrained when axis: x" ); + } + }), + item = element.find( "li" ).eq( 0 ), + offsetBefore = item.offset(); + + item.simulate( "drag", { + dx: 50, + dy: 25, + moves: 1 + }); +}); + +test( "{ axis: 'y' }", function() { + expect( 2 ); + + var offsetAfter, + element = $( "#sortable" ).sortable({ + axis: "y", + change: function() { + offsetAfter = item.offset(); + equal( offsetAfter.left, offsetBefore.left, "x axis constrained when axis: y" ); + notEqual( offsetAfter.top, offsetBefore.top, "y axis not constrained when axis: y" ); + } + }), + item = element.find( "li" ).eq( 0 ), + offsetBefore = item.offset(); + + item.simulate( "drag", { + dx: 50, + dy: 25, + moves: 1 + }); +}); + +asyncTest( "#7415: Incorrect revert animation with axis: 'y'", function() { + expect( 2 ); + var expectedLeft, + element = $( "#sortable" ).sortable({ + axis: "y", + revert: true, + stop: start, + sort: function() { + expectedLeft = item.css( "left" ); + } + }), + item = element.find( "li" ).eq( 0 ); + + item.simulate( "drag", { + dy: 300, + dx: 50 + }); + + setTimeout(function() { + var top = parseFloat( item.css( "top" ) ); + equal( item.css( "left" ), expectedLeft, "left not animated" ); + ok( top > 0 && top < 300, "top is animated" ); + }, 100 ); +}); + +/* +test("{ cancel: 'input,textarea,button,select,option' }, default", function() { + ok(false, "missing test - untested code is broken code."); +}); + +test("{ cancel: Selector }", function() { + ok(false, "missing test - untested code is broken code."); +}); +*/ + +test( "#8792: issues with floated items in connected lists", function() { + expect( 2 ); + + var element, + changeCount = 0; + + $( "#qunit-fixture" ) + .html( "<ul class='c'><li>a</li><li>a</li></ul><ul class='c'><li>a</li><li>a</li></ul>" ) + .find( "ul" ).css({ "float": "left", width: "100px" }).end() + .find( "li" ).css({ "float": "left", width: "50px", height: "50px" }); + + $( "#qunit-fixture .c" ).sortable({ + connectWith: "#qunit-fixture .c", + change: function() { + changeCount++; + } + }); + + element = $( "#qunit-fixture li:eq(0)" ); + + // move the first li to the right of the second li in the first ul + element.simulate( "drag", { + dx: 55, + moves: 15 + }); + + equal( changeCount, 1, "change fired only once (no jitters) when dragging a floated sortable in it's own container" ); + + // move the first li ( which is now in the second spot ) + // through the first spot in the second ul to the second spot in the second ul + element.simulate( "drag", { + dx: 100, + moves: 15 + }); + + equal( changeCount, 3, "change fired once for each expected change when dragging a floated sortable to a connected container" ); +}); + +test( "#8301: single axis with connected list", function() { + expect( 1 ); + + var element = $( "#sortable" ).sortable({ + axis: "y", + tolerance: "pointer", + connectWith: ".connected" + }); + + $( "<ul class='connected'><li>Item 7</li><li>Item 8</li></ul>" ) + .sortable({ + axis: "y", + tolerance: "pointer", + connectWith: "#sortable", + receive: function() { + ok( true, "connected list received item" ); + } + }) + .insertAfter( element ); + + element.find( "li" ).eq( 0 ).simulate( "drag", { + handle: "corner", + dy: 120, + moves: 1 + }); +}); + +/* +test("{ connectWith: false }, default", function() { + ok(false, "missing test - untested code is broken code."); +}); + +test("{ connectWith: Selector }", function() { + ok(false, "missing test - untested code is broken code."); +}); + +test("{ containment: false }, default", function() { + ok(false, "missing test - untested code is broken code."); +}); + +test("{ containment: Element }", function() { + ok(false, "missing test - untested code is broken code."); +}); + +test("{ containment: 'document' }", function() { + ok(false, "missing test - untested code is broken code."); +}); + +test("{ containment: 'parent' }", function() { + ok(false, "missing test - untested code is broken code."); +}); + +test("{ containment: 'window' }", function() { + ok(false, "missing test - untested code is broken code."); +}); + +test("{ containment: Selector }", function() { + ok(false, "missing test - untested code is broken code."); +}); + +test("{ cursor: 'auto' }, default", function() { + ok(false, "missing test - untested code is broken code."); +}); + +test("{ cursor: 'move' }", function() { + ok(false, "missing test - untested code is broken code."); +}); + +test("{ cursorAt: false }, default", function() { + ok(false, "missing test - untested code is broken code."); +}); + +test("{ cursorAt: true }", function() { + ok(false, "missing test - untested code is broken code."); +}); + +test("{ delay: 0 }, default", function() { + ok(false, "missing test - untested code is broken code."); +}); + +test("{ delay: 100 }", function() { + ok(false, "missing test - untested code is broken code."); +}); + +test("{ distance: 1 }, default", function() { + ok(false, "missing test - untested code is broken code."); +}); + +test("{ distance: 10 }", function() { + ok(false, "missing test - untested code is broken code."); +}); + +test("{ dropOnEmpty: true }, default", function() { + ok(false, "missing test - untested code is broken code."); +}); + +test("{ dropOnEmpty: false }", function() { + ok(false, "missing test - untested code is broken code."); +}); + +test("{ forcePlaceholderSize: false }, default", function() { + ok(false, "missing test - untested code is broken code."); +}); + +test("{ forcePlaceholderSize: true }", function() { + ok(false, "missing test - untested code is broken code."); +}); + +test("{ forceHelperSize: false }, default", function() { + ok(false, "missing test - untested code is broken code."); +}); + +test("{ forceHelperSize: true }", function() { + ok(false, "missing test - untested code is broken code."); +}); + +test("{ grid: false }, default", function() { + ok(false, "missing test - untested code is broken code."); +}); + +test("{ grid: [17, 3] }", function() { + ok(false, "missing test - untested code is broken code."); +}); + +test("{ grid: [3, 7] }", function() { + ok(false, "missing test - untested code is broken code."); +}); + +test("{ handle: false }, default", function() { + ok(false, "missing test - untested code is broken code."); +}); + +test("{ handle: Element }", function() { + ok(false, "missing test - untested code is broken code."); +}); + +test("{ handle: Selector }", function() { + ok(false, "missing test - untested code is broken code."); +}); + +test("{ helper: 'original' }, default", function() { + ok(false, "missing test - untested code is broken code."); +}); + +test("{ helper: Function }", function() { + ok(false, "missing test - untested code is broken code."); +}); + +test("{ items: '> *' }, default", function() { + ok(false, "missing test - untested code is broken code."); +}); + +test("{ items: Selector }", function() { + ok(false, "missing test - untested code is broken code."); +}); + +test("{ opacity: false }, default", function() { + ok(false, "missing test - untested code is broken code."); +}); + +test("{ opacity: .37 }", function() { + ok(false, "missing test - untested code is broken code."); +}); + +test("{ opacity: 1 }", function() { + ok(false, "missing test - untested code is broken code."); +}); + +test("{ placeholder: false }, default", function() { + ok(false, "missing test - untested code is broken code."); +}); +*/ + +test( "{ placeholder: false } img", function() { + expect( 3 ); + + var element = $( "#sortable-images" ).sortable({ + start: function( event, ui ) { + ok( ui.placeholder.attr( "src" ).indexOf( "images/jqueryui_32x32.png" ) > 0, "placeholder img has correct src" ); + equal( ui.placeholder.height(), 32, "placeholder has correct height" ); + equal( ui.placeholder.width(), 32, "placeholder has correct width" ); + } + }); + + element.find( "img" ).eq( 0 ).simulate( "drag", { + dy: 1 + }); +}); + +test( "{ placeholder: String }", function() { + expect( 1 ); + + var element = $( "#sortable" ).sortable({ + placeholder: "test", + start: function( event, ui ) { + ok( ui.placeholder.hasClass( "test" ), "placeholder has class" ); + } + }); + + element.find( "li" ).eq( 0 ).simulate( "drag", { + dy: 1 + }); +}); + +test( "{ placholder: String } tr", function() { + expect( 4 ); + + var originalWidths, + element = $( "#sortable-table tbody" ).sortable({ + placeholder: "test", + start: function( event, ui ) { + var currentWidths = otherRow.children().map(function() { + return $( this ).width(); + }).get(); + ok( ui.placeholder.hasClass( "test" ), "placeholder has class" ); + deepEqual( currentWidths, originalWidths, "table cells maintian size" ); + equal( ui.placeholder.children().length, dragRow.children().length, + "placeholder has correct number of cells" ); + equal( ui.placeholder.children().html(), $( "<span> </span>" ).html(), + "placeholder td has content for forced dimensions" ); + } + }), + rows = element.children( "tr" ), + dragRow = rows.eq( 0 ), + otherRow = rows.eq( 1 ); + + originalWidths = otherRow.children().map(function() { + return $( this ).width(); + }).get(); + dragRow.simulate( "drag", { + dy: 1 + }); +}); + +/* +test("{ revert: false }, default", function() { + ok(false, "missing test - untested code is broken code."); +}); + +test("{ revert: true }", function() { + ok(false, "missing test - untested code is broken code."); +}); + +test("{ scroll: true }, default", function() { + ok(false, "missing test - untested code is broken code."); +}); + +test("{ scroll: false }", function() { + ok(false, "missing test - untested code is broken code."); +}); + +test("{ scrollSensitivity: 20 }, default", function() { + ok(false, "missing test - untested code is broken code."); +}); + +test("{ scrollSensitivity: 2 }", function() { + ok(false, "missing test - untested code is broken code."); +}); + +test("{ scrollSensitivity: 200 }", function() { + ok(false, "missing test - untested code is broken code."); +}); + +test("{ scrollSpeed: 20 }, default", function() { + ok(false, "missing test - untested code is broken code."); +}); + +test("{ scrollSpeed: 2 }", function() { + ok(false, "missing test - untested code is broken code."); +}); + +test("{ scrollSpeed: 200 }", function() { + ok(false, "missing test - untested code is broken code."); +}); + +test("{ scope: 'default' }, default", function() { + ok(false, "missing test - untested code is broken code."); +}); + +test("{ scope: ??? }, unexpected", function() { + ok(false, "missing test - untested code is broken code."); +}); + +test("{ tolerance: 'intersect' }, default", function() { + ok(false, "missing test - untested code is broken code."); +}); + +test("{ tolerance: 'pointer' }", function() { + ok(false, "missing test - untested code is broken code."); +}); + +test("{ zIndex: 1000 }, default", function() { + ok(false, "missing test - untested code is broken code."); +}); + +test("{ zIndex: 1 }", function() { + ok(false, "missing test - untested code is broken code."); +}); + +test("{ zIndex: false }", function() { + ok(false, "missing test - untested code is broken code."); +}); +*/ +})(jQuery); diff --git a/apps/it/static/js/ui/tests/unit/sortable/sortable_test_helpers.js b/apps/it/static/js/ui/tests/unit/sortable/sortable_test_helpers.js new file mode 100644 index 0000000..12e4829 --- /dev/null +++ b/apps/it/static/js/ui/tests/unit/sortable/sortable_test_helpers.js @@ -0,0 +1,9 @@ +TestHelpers.sortable = { + sort: function( handle, dx, dy, index, msg ) { + $( handle ).simulate( "drag", { + dx: dx, + dy: dy + }); + equal( $( handle ).parent().children().index( handle ), index, msg ); + } +};
\ No newline at end of file diff --git a/apps/it/static/js/ui/tests/unit/spinner/all.html b/apps/it/static/js/ui/tests/unit/spinner/all.html new file mode 100644 index 0000000..bc180d4 --- /dev/null +++ b/apps/it/static/js/ui/tests/unit/spinner/all.html @@ -0,0 +1,30 @@ +<!doctype html> +<html lang="en"> +<head> + <meta charset="utf-8"> + <title>jQuery UI Spinner Test Suite</title> + + <script src="../../../jquery-1.10.2.js"></script> + + <link rel="stylesheet" href="../../../external/qunit.css"> + <link rel="stylesheet" href="../qunit-composite.css"> + <script src="../../../external/qunit.js"></script> + <script src="../qunit-composite.js"></script> + <script src="../subsuite.js"></script> + + <script> + testAllVersions( "spinner" ); + </script> +</head> +<body> + +<h1 id="qunit-header">jQuery UI Spinner Test Suite</h1> +<h2 id="qunit-banner"></h2> +<div id="qunit-testrunner-toolbar"></div> +<h2 id="qunit-userAgent"></h2> +<ol id="qunit-tests"></ol> +<div id="qunit-fixture"> + +</div> +</body> +</html> diff --git a/apps/it/static/js/ui/tests/unit/spinner/spinner.html b/apps/it/static/js/ui/tests/unit/spinner/spinner.html new file mode 100644 index 0000000..1d09bbc --- /dev/null +++ b/apps/it/static/js/ui/tests/unit/spinner/spinner.html @@ -0,0 +1,50 @@ +<!doctype html> +<html lang="en"> +<head> + <meta charset="utf-8"> + <title>jQuery UI Spinner Test Suite</title> + + <script src="../../jquery.js"></script> + <script src="../../../external/jquery.mousewheel.js"></script> + <script src="../../../external/globalize.js"></script> + <script src="../../../external/globalize.culture.ja-JP.js"></script> + <link rel="stylesheet" href="../../../external/qunit.css"> + <script src="../../../external/qunit.js"></script> + <script src="../../jquery.simulate.js"></script> + <script src="../testsuite.js"></script> + <script> + TestHelpers.loadResources({ + css: [ "ui.core", "ui.button", "ui.spinner" ], + js: [ + "ui/jquery.ui.core.js", + "ui/jquery.ui.widget.js", + "ui/jquery.ui.button.js", + "ui/jquery.ui.spinner.js" + ] + }); + </script> + + <script src="spinner_test_helpers.js"></script> + <script src="spinner_common.js"></script> + <script src="spinner_core.js"></script> + <script src="spinner_events.js"></script> + <script src="spinner_methods.js"></script> + <script src="spinner_options.js"></script> + + <script src="../swarminject.js"></script> +</head> +<body> + +<h1 id="qunit-header">jQuery UI Spinner Test Suite</h1> +<h2 id="qunit-banner"></h2> +<div id="qunit-testrunner-toolbar"></div> +<h2 id="qunit-userAgent"></h2> +<ol id="qunit-tests"></ol> +<div id="qunit-fixture"> + +<input id="spin" class="foo"> +<input id="spin2" value="2"> + +</div> +</body> +</html> diff --git a/apps/it/static/js/ui/tests/unit/spinner/spinner_common.js b/apps/it/static/js/ui/tests/unit/spinner/spinner_common.js new file mode 100644 index 0000000..b494e3c --- /dev/null +++ b/apps/it/static/js/ui/tests/unit/spinner/spinner_common.js @@ -0,0 +1,23 @@ +TestHelpers.commonWidgetTests( "spinner", { + defaults: { + culture: null, + disabled: false, + icons: { + down: "ui-icon-triangle-1-s", + up: "ui-icon-triangle-1-n" + }, + incremental: true, + max: null, + min: null, + numberFormat: null, + page: 10, + step: 1, + + // callbacks + change: null, + create: null, + spin: null, + start: null, + stop: null + } +}); diff --git a/apps/it/static/js/ui/tests/unit/spinner/spinner_core.js b/apps/it/static/js/ui/tests/unit/spinner/spinner_core.js new file mode 100644 index 0000000..03088fe --- /dev/null +++ b/apps/it/static/js/ui/tests/unit/spinner/spinner_core.js @@ -0,0 +1,238 @@ +(function( $ ) { + +var simulateKeyDownUp = TestHelpers.spinner.simulateKeyDownUp; + +module( "spinner: core" ); + +test( "keydown UP on input, increases value not greater than max", function() { + expect( 5 ); + var element = $( "#spin" ).val( 70 ).spinner({ + max: 100, + step: 10 + }); + + simulateKeyDownUp( element, $.ui.keyCode.UP ); + equal( element.val(), 80 ); + simulateKeyDownUp( element, $.ui.keyCode.UP ); + equal( element.val(), 90 ); + simulateKeyDownUp( element, $.ui.keyCode.UP ); + equal( element.val(), 100 ); + simulateKeyDownUp( element, $.ui.keyCode.UP ); + equal( element.val(), 100 ); + simulateKeyDownUp( element, $.ui.keyCode.UP ); + equal( element.val(), 100 ); +}); + +test( "keydown DOWN on input, decreases value not less than min", function() { + expect( 5 ); + var element = $( "#spin" ).val( 50 ).spinner({ + min: 20, + step: 10 + }); + + simulateKeyDownUp( element, $.ui.keyCode.DOWN ); + equal( element.val(), 40 ); + simulateKeyDownUp( element, $.ui.keyCode.DOWN ); + equal( element.val(), 30 ); + simulateKeyDownUp( element, $.ui.keyCode.DOWN ); + equal( element.val(), 20 ); + simulateKeyDownUp( element, $.ui.keyCode.DOWN ); + equal( element.val(), 20 ); + simulateKeyDownUp( element, $.ui.keyCode.DOWN ); + equal( element.val(), 20 ); +}); + +test( "keydown PAGE_UP on input, increases value not greater than max", function() { + expect( 5 ); + var element = $( "#spin" ).val( 70 ).spinner({ + max: 100, + page: 10 + }); + + simulateKeyDownUp( element, $.ui.keyCode.PAGE_UP ); + equal( element.val(), 80 ); + simulateKeyDownUp( element, $.ui.keyCode.PAGE_UP ); + equal( element.val(), 90 ); + simulateKeyDownUp( element, $.ui.keyCode.PAGE_UP ); + equal( element.val(), 100 ); + simulateKeyDownUp( element, $.ui.keyCode.PAGE_UP ); + equal( element.val(), 100 ); + simulateKeyDownUp( element, $.ui.keyCode.PAGE_UP ); + equal( element.val(), 100 ); +}); + +test( "keydown PAGE_DOWN on input, decreases value not less than min", function() { + expect( 5 ); + var element = $( "#spin" ).val( 50 ).spinner({ + min: 20, + page: 10 + }); + + simulateKeyDownUp( element, $.ui.keyCode.PAGE_DOWN ); + equal( element.val(), 40 ); + simulateKeyDownUp( element, $.ui.keyCode.PAGE_DOWN ); + equal( element.val(), 30 ); + simulateKeyDownUp( element, $.ui.keyCode.PAGE_DOWN ); + equal( element.val(), 20 ); + simulateKeyDownUp( element, $.ui.keyCode.PAGE_DOWN ); + equal( element.val(), 20 ); + simulateKeyDownUp( element, $.ui.keyCode.PAGE_DOWN ); + equal( element.val(), 20 ); +}); + +asyncTest( "blur input while spinning with UP", function() { + expect( 3 ); + var value, + element = $( "#spin" ).val( 10 ).spinner(); + + function step1() { + element.simulate( "keydown", { keyCode: $.ui.keyCode.UP } ); + equal( element.val(), 11 ); + setTimeout( step2, 750 ); + } + + function step2() { + value = element.val(); + ok( value > 11, "repeating while key is down" ); + + element.bind( "blur", function() { + value = element.val(); + setTimeout( step3, 750 ); + })[ 0 ].blur(); + } + + function step3() { + equal( element.val(), value, "stopped repeating on blur" ); + start(); + } + + element[ 0 ].focus(); + setTimeout( step1 ); +}); + +test( "mouse click on up button, increases value not greater than max", function() { + expect( 3 ); + var element = $( "#spin" ).val( 18 ).spinner({ + max: 20 + }), + button = element.spinner( "widget" ).find( ".ui-spinner-up" ); + + button.trigger( "mousedown" ).trigger( "mouseup" ); + equal( element.val(), 19 ); + button.trigger( "mousedown" ).trigger( "mouseup" ); + equal( element.val(), 20 ); + button.trigger( "mousedown" ).trigger( "mouseup" ); + equal( element.val(), 20 ); +}); + +test( "mouse click on up button, increases value not greater than max", function() { + expect( 3 ); + var element = $( "#spin" ).val( 2 ).spinner({ + min: 0 + }), + button = element.spinner( "widget" ).find( ".ui-spinner-down" ); + + button.trigger( "mousedown" ).trigger( "mouseup" ); + equal( element.val(), 1 ); + button.trigger( "mousedown" ).trigger( "mouseup" ); + equal( element.val(), 0 ); + button.trigger( "mousedown" ).trigger( "mouseup" ); + equal( element.val(), 0 ); +}); + +test( "mousewheel on input", function() { + expect( 4 ); + + var element = $( "#spin" ).val( 0 ).spinner({ + step: 2 + }); + + element.trigger( "mousewheel" ); + equal( element.val(), 0, "mousewheel event without delta does not change value" ); + + element.trigger( "mousewheel", 1 ); + equal( element.val(), 2 ); + + element.trigger( "mousewheel", -0.2 ); + equal( element.val(), 0 ); + + element.trigger( "mousewheel", -15 ); + equal(element.val(), -2 ); +}); + +test( "reading HTML5 attributes", function() { + expect( 6 ); + var markup = "<input type='number' min='-100' max='100' value='5' step='2'>", + element = $( markup ).spinner(); + equal( element.spinner( "option", "min" ), -100, "min from markup" ); + equal( element.spinner( "option", "max" ), 100, "max from markup" ); + equal( element.spinner( "option", "step" ), 2, "step from markup" ); + + element = $( markup ).spinner({ + min: -200, + max: 200, + step: 5 + }); + equal( element.spinner( "option", "min" ), -200, "min from options" ); + equal( element.spinner( "option", "max" ), 200, "max from options" ); + equal( element.spinner( "option", "step" ), 5, "stop from options" ); +}); + +test( "ARIA attributes", function() { + expect( 9 ); + var element = $( "#spin" ).val( 2 ).spinner({ min: -5, max: 5 }); + + equal( element.attr( "role" ), "spinbutton", "role" ); + equal( element.attr( "aria-valuemin" ), "-5", "aria-valuemin" ); + equal( element.attr( "aria-valuemax" ), "5", "aria-valuemax" ); + equal( element.attr( "aria-valuenow" ), "2", "aria-valuenow" ); + + element.spinner( "stepUp" ); + + equal( element.attr( "aria-valuenow" ), "3", "stepUp 1 step changes aria-valuenow" ); + + element.spinner( "option", { min: -10, max: 10 } ); + + equal( element.attr( "aria-valuemin" ), "-10", "min option changed aria-valuemin changes" ); + equal( element.attr( "aria-valuemax" ), "10", "max option changed aria-valuemax changes" ); + + element.spinner( "option", "min", null ); + equal( element.attr( "aria-valuemin" ), undefined, "aria-valuemin not set when no min" ); + + element.spinner( "option", "max", null ); + equal( element.attr( "aria-valuemax" ), undefined, "aria-valuemax not set when no max" ); +}); + +test( "focus text field when pressing button", function() { + expect( 2 ); + var element = $( "#spin" ).spinner(); + $( "body" ).focus(); + ok( element[ 0 ] !== document.activeElement, "not focused before" ); + element.spinner( "widget" ).find( ".ui-spinner-up" ).mousedown(); + ok( element[ 0 ] === document.activeElement, "focused after" ); +}); + +test( "don't clear invalid value on blur", function() { + expect( 1 ); + var element = $( "#spin" ).spinner(); + element.focus().val( "a" ).blur(); + equal( element.val(), "a" ); +}); + +test( "precision", function() { + expect( 2 ); + var element = $( "#spin" ).val( 0.05 ).spinner({ + step: 0.0001 + }); + element.spinner( "stepUp" ); + equal( element.val(), "0.0501", "precision from step" ); + + element.val( 1.05 ).spinner( "option", { + step: 1, + min: -9.95 + }); + element.spinner( "stepDown" ); + equal( element.val(), "0.05", "precision from min" ); +}); + +})( jQuery ); diff --git a/apps/it/static/js/ui/tests/unit/spinner/spinner_events.js b/apps/it/static/js/ui/tests/unit/spinner/spinner_events.js new file mode 100644 index 0000000..610f7a2 --- /dev/null +++ b/apps/it/static/js/ui/tests/unit/spinner/spinner_events.js @@ -0,0 +1,259 @@ +(function( $ ) { + +var simulateKeyDownUp = TestHelpers.spinner.simulateKeyDownUp; + +module( "spinner: events" ); + +test( "start", function() { + expect( 10 ); + var element = $( "#spin" ).spinner(); + + function shouldStart( expectation, msg ) { + element.spinner( "option", "start", function() { + ok( expectation, msg ); + }); + } + + shouldStart( true, "key UP" ); + simulateKeyDownUp( element, $.ui.keyCode.UP ); + shouldStart( true, "key DOWN" ); + simulateKeyDownUp( element, $.ui.keyCode.DOWN ); + + shouldStart( true, "key PAGE_UP" ); + simulateKeyDownUp( element, $.ui.keyCode.PAGE_UP ); + shouldStart( true, "key PAGE_DOWN" ); + simulateKeyDownUp( element, $.ui.keyCode.PAGE_DOWN ); + + shouldStart( true, "button up" ); + element.spinner( "widget" ).find( ".ui-spinner-up" ).mousedown().mouseup(); + shouldStart( true, "button down" ); + element.spinner( "widget" ).find( ".ui-spinner-down" ).mousedown().mouseup(); + + shouldStart( true, "stepUp" ); + element.spinner( "stepUp" ); + shouldStart( true, "stepDown" ); + element.spinner( "stepDown" ); + + shouldStart( true, "pageUp" ); + element.spinner( "pageUp" ); + shouldStart( true, "pageDown" ); + element.spinner( "pageDown" ); + + shouldStart( false, "value" ); + element.spinner( "value", 999 ); +}); + +test( "spin", function() { + expect( 10 ); + var element = $( "#spin" ).spinner(); + + function shouldSpin( expectation, msg ) { + element.spinner( "option", "spin", function() { + ok( expectation, msg ); + }); + } + + shouldSpin( true, "key UP" ); + simulateKeyDownUp( element, $.ui.keyCode.UP ); + shouldSpin( true, "key DOWN" ); + simulateKeyDownUp( element, $.ui.keyCode.DOWN ); + + shouldSpin( true, "key PAGE_UP" ); + simulateKeyDownUp( element, $.ui.keyCode.PAGE_UP ); + shouldSpin( true, "key PAGE_DOWN" ); + simulateKeyDownUp( element, $.ui.keyCode.PAGE_DOWN ); + + shouldSpin( true, "button up" ); + element.spinner( "widget" ).find( ".ui-spinner-up" ).mousedown().mouseup(); + shouldSpin( true, "button down" ); + element.spinner( "widget" ).find( ".ui-spinner-down" ).mousedown().mouseup(); + + shouldSpin( true, "stepUp" ); + element.spinner( "stepUp" ); + shouldSpin( true, "stepDown" ); + element.spinner( "stepDown" ); + + shouldSpin( true, "pageUp" ); + element.spinner( "pageUp" ); + shouldSpin( true, "pageDown" ); + element.spinner( "pageDown" ); + + shouldSpin( false, "value" ); + element.spinner( "value", 999 ); +}); + +test( "stop", function() { + expect( 10 ); + var element = $( "#spin" ).spinner(); + + function shouldStop( expectation, msg ) { + element.spinner( "option", "stop", function() { + ok( expectation, msg ); + }); + } + + shouldStop( true, "key UP" ); + simulateKeyDownUp( element, $.ui.keyCode.UP ); + shouldStop( true, "key DOWN" ); + simulateKeyDownUp( element, $.ui.keyCode.DOWN ); + + shouldStop( true, "key PAGE_UP" ); + simulateKeyDownUp( element, $.ui.keyCode.PAGE_UP ); + shouldStop( true, "key PAGE_DOWN" ); + simulateKeyDownUp( element, $.ui.keyCode.PAGE_DOWN ); + + shouldStop( true, "button up" ); + element.spinner( "widget" ).find( ".ui-spinner-up" ).mousedown().mouseup(); + shouldStop( true, "button down" ); + element.spinner( "widget" ).find( ".ui-spinner-down" ).mousedown().mouseup(); + + shouldStop( true, "stepUp" ); + element.spinner( "stepUp" ); + shouldStop( true, "stepDown" ); + element.spinner( "stepDown" ); + + shouldStop( true, "pageUp" ); + element.spinner( "pageUp" ); + shouldStop( true, "pageDown" ); + element.spinner( "pageDown" ); + + shouldStop( false, "value" ); + element.spinner( "value", 999 ); +}); + +asyncTest( "change", function() { + expect( 14 ); + var element = $( "#spin" ).spinner(); + + function shouldChange( expectation, msg ) { + element.spinner( "option", "change", function() { + ok( expectation, msg ); + }); + } + + function focusWrap( fn, next ) { + element[0].focus(); + setTimeout( function() { + fn(); + setTimeout(function() { + element[0].blur(); + setTimeout( next ); + }); + }); + } + + function step1() { + focusWrap(function() { + shouldChange( false, "key UP, before blur" ); + simulateKeyDownUp( element, $.ui.keyCode.UP ); + shouldChange( true, "blur after key UP" ); + }, step2 ); + } + + function step2() { + focusWrap(function() { + shouldChange( false, "key DOWN, before blur" ); + simulateKeyDownUp( element, $.ui.keyCode.DOWN ); + shouldChange( true, "blur after key DOWN" ); + }, step3 ); + } + + function step3() { + focusWrap(function() { + shouldChange( false, "key PAGE_UP, before blur" ); + simulateKeyDownUp( element, $.ui.keyCode.PAGE_UP ); + shouldChange( true, "blur after key PAGE_UP" ); + }, step4 ); + } + + function step4() { + focusWrap(function() { + shouldChange( false, "key PAGE_DOWN, before blur" ); + simulateKeyDownUp( element, $.ui.keyCode.PAGE_DOWN ); + shouldChange( true, "blur after key PAGE_DOWN" ); + }, step5 ); + } + + function step5() { + focusWrap(function() { + shouldChange( false, "many keys, before blur" ); + simulateKeyDownUp( element, $.ui.keyCode.PAGE_DOWN ); + simulateKeyDownUp( element, $.ui.keyCode.UP ); + simulateKeyDownUp( element, $.ui.keyCode.UP ); + simulateKeyDownUp( element, $.ui.keyCode.UP ); + simulateKeyDownUp( element, $.ui.keyCode.DOWN ); + shouldChange( true, "blur after many keys" ); + }, step6 ); + } + + function step6() { + focusWrap(function() { + shouldChange( false, "many keys, same final value, before blur" ); + simulateKeyDownUp( element, $.ui.keyCode.UP ); + simulateKeyDownUp( element, $.ui.keyCode.UP ); + simulateKeyDownUp( element, $.ui.keyCode.DOWN ); + simulateKeyDownUp( element, $.ui.keyCode.DOWN ); + shouldChange( false, "blur after many keys, same final value" ); + + shouldChange( false, "button up, before blur" ); + element.spinner( "widget" ).find( ".ui-spinner-up" ).mousedown().mouseup(); + shouldChange( true, "blur after button up" ); + }, step7 ); + } + + function step7() { + focusWrap(function() { + shouldChange( false, "button down, before blur" ); + element.spinner( "widget" ).find( ".ui-spinner-down" ).mousedown().mouseup(); + shouldChange( true, "blur after button down" ); + }, step8 ); + } + + function step8() { + focusWrap(function() { + shouldChange( false, "many buttons, same final value, before blur" ); + element.spinner( "widget" ).find( ".ui-spinner-up" ).mousedown().mouseup(); + element.spinner( "widget" ).find( ".ui-spinner-up" ).mousedown().mouseup(); + element.spinner( "widget" ).find( ".ui-spinner-down" ).mousedown().mouseup(); + element.spinner( "widget" ).find( ".ui-spinner-down" ).mousedown().mouseup(); + shouldChange( false, "blur after many buttons, same final value" ); + }, step9 ); + } + + function step9() { + shouldChange( true, "stepUp" ); + element.spinner( "stepUp" ); + + shouldChange( true, "stepDown" ); + element.spinner( "stepDown" ); + + shouldChange( true, "pageUp" ); + element.spinner( "pageUp" ); + + shouldChange( true, "pageDown" ); + element.spinner( "pageDown" ); + + shouldChange( true, "value" ); + element.spinner( "value", 999 ); + + shouldChange( false, "value, same value" ); + element.spinner( "value", 999 ); + + shouldChange( true, "max, value changed" ); + element.spinner( "option", "max", 900 ); + + shouldChange( false, "max, value not changed" ); + element.spinner( "option", "max", 1000 ); + + shouldChange( true, "min, value changed" ); + element.spinner( "option", "min", 950 ); + + shouldChange( false, "min, value not changed" ); + element.spinner( "option", "min", 200 ); + start(); + } + + setTimeout( step1 ); +}); + +})( jQuery ); diff --git a/apps/it/static/js/ui/tests/unit/spinner/spinner_methods.js b/apps/it/static/js/ui/tests/unit/spinner/spinner_methods.js new file mode 100644 index 0000000..af872f2 --- /dev/null +++ b/apps/it/static/js/ui/tests/unit/spinner/spinner_methods.js @@ -0,0 +1,174 @@ +(function( $ ) { + +var simulateKeyDownUp = TestHelpers.spinner.simulateKeyDownUp; + +module( "spinner: methods" ); + +test( "destroy", function() { + expect( 1 ); + domEqual( "#spin", function() { + $( "#spin" ).spinner().spinner( "destroy" ); + }); +}); + +test( "disable", function() { + expect( 14 ); + var element = $( "#spin" ).val( 2 ).spinner(), + wrapper = $( "#spin" ).spinner( "widget" ); + + ok( !wrapper.hasClass( "ui-spinner-disabled" ), "before: wrapper does not have ui-spinner-disabled class" ); + ok( !element.is( ":disabled" ), "before: input does not have disabled attribute" ); + + element.spinner( "disable" ); + ok( wrapper.hasClass( "ui-spinner-disabled" ), "after: wrapper has ui-spinner-disabled class" ); + ok( element.is( ":disabled"), "after: input has disabled attribute" ); + + simulateKeyDownUp( element, $.ui.keyCode.UP ); + equal( 2, element.val(), "keyboard - value does not change on key UP" ); + + simulateKeyDownUp( element, $.ui.keyCode.DOWN ); + equal( 2, element.val(), "keyboard - value does not change on key DOWN" ); + + simulateKeyDownUp( element, $.ui.keyCode.PAGE_UP ); + equal( 2, element.val(), "keyboard - value does not change on key PGUP" ); + + simulateKeyDownUp( element, $.ui.keyCode.PAGE_DOWN ); + equal( 2, element.val(), "keyboard - value does not change on key PGDN" ); + + wrapper.find( ".ui-spinner-up" ).trigger( "mousedown" ).trigger( "mouseup" ); + equal( 2, element.val(), "mouse - value does not change on clicking up button" ); + + wrapper.find( ".ui-spinner-down" ).trigger( "mousedown" ).trigger( "mouseup" ); + equal( 2, element.val(), "mouse - value does not change on clicking down button" ); + + element.spinner( "stepUp", 6 ); + equal( 8, element.val(), "script - stepUp 6 steps changes value"); + + element.spinner( "stepDown" ); + equal( 7, element.val(), "script - stepDown 1 step changes value" ); + + element.spinner( "pageUp" ); + equal( 17, element.val(), "script - pageUp 1 page changes value" ); + + element.spinner( "pageDown" ); + equal( 7, element.val(), "script - pageDown 1 page changes value" ); +}); + +test( "enable", function() { + expect( 5 ); + var element = $( "#spin" ).val( 1 ).spinner({ disabled: true }), + wrapper = element.spinner( "widget" ); + + ok( wrapper.hasClass( "ui-spinner-disabled" ), "before: wrapper has ui-spinner-disabled class" ); + ok( element.is( ":disabled" ), "before: input has disabled attribute" ); + + element.spinner( "enable" ); + + ok( !wrapper.hasClass( ".ui-spinner-disabled" ), "after: wrapper does not have ui-spinner-disabled class" ); + ok( !element.is( ":disabled" ), "after: input does not have disabled attribute" ); + + simulateKeyDownUp( element, $.ui.keyCode.UP ); + equal( 2, element.val(), "keyboard - value changes on key UP" ); +}); + +test( "pageDown", function() { + expect( 4 ); + var element = $( "#spin" ).val( -12 ).spinner({ + page: 20, + min: -100 + }); + + element.spinner( "pageDown" ); + equal( element.val(), -32, "pageDown 1 page" ); + + element.spinner( "pageDown", 3 ); + equal( element.val(), -92, "pageDown 3 pages" ); + + element.spinner( "pageDown" ); + equal( element.val(), -100, "value close to min and pageDown 1 page" ); + + element.spinner( "pageDown", 10 ); + equal( element.val(), -100, "value at min and pageDown 10 pages" ); +}); + +test( "pageUp", function() { + expect( 4 ); + var element = $( "#spin" ).val( 12 ).spinner({ + page: 20, + max: 100 + }); + + element.spinner( "pageUp" ); + equal( element.val(), 32, "pageUp 1 page" ); + + element.spinner( "pageUp", 3 ); + equal( element.val(), 92, "pageUp 3 pages" ); + + element.spinner( "pageUp" ); + equal( element.val(), 100, "value close to max and pageUp 1 page" ); + + element.spinner( "pageUp", 10 ); + equal( element.val(), 100, "value at max and pageUp 10 pages" ); +}); + +test( "stepDown", function() { + expect( 4 ); + var element = $( "#spin" ).val( 0 ).spinner({ + step: 2, + min: -15 + }); + + element.spinner( "stepDown" ); + equal( element.val(), "-1", "stepDown 1 step" ); + + element.spinner( "stepDown", 5 ); + equal( element.val(), "-11", "stepDown 5 steps" ); + + element.spinner( "stepDown", 4 ); + equal( element.val(), "-15", "close to min and stepDown 4 steps" ); + + element.spinner( "stepDown" ); + equal( element.val(), "-15", "at min and stepDown 1 step" ); +}); + +test( "stepUp", function() { + expect( 4 ); + var element = $( "#spin" ).val( 0 ).spinner({ + step: 2, + max: 16 + }); + + element.spinner( "stepUp" ); + equal( element.val(), 2, "stepUp 1 step" ); + + element.spinner( "stepUp", 5 ); + equal( element.val(), 12, "stepUp 5 steps" ); + + element.spinner( "stepUp", 4 ); + equal( element.val(), 16, "close to min and stepUp 4 steps" ); + + element.spinner( "stepUp" ); + equal( element.val(), 16, "at max and stepUp 1 step" ); +}); + +test( "value", function() { + expect( 2 ); + var element = $( "#spin" ).val( 0 ).spinner({ + step: 3 + }); + + element.spinner( "value", 10 ); + equal( element.val(), 9, "change value via value method" ); + + equal( element.spinner( "value" ), 9, "get value via value method" ); +}); + +test( "widget", function() { + expect( 2 ); + var element = $( "#spin" ).spinner(), + widgetElement = element.spinner( "widget" ); + equal( widgetElement.length, 1, "one element" ); + strictEqual( widgetElement[ 0 ], element.parent()[ 0 ], "parent element" ); +}); + +})( jQuery ); diff --git a/apps/it/static/js/ui/tests/unit/spinner/spinner_options.js b/apps/it/static/js/ui/tests/unit/spinner/spinner_options.js new file mode 100644 index 0000000..6f3650f --- /dev/null +++ b/apps/it/static/js/ui/tests/unit/spinner/spinner_options.js @@ -0,0 +1,262 @@ +(function( $ ) { + +module( "spinner: options" ); + +// culture is tested after numberFormat, since it depends on numberFormat + +test( "icons: default ", function() { + expect( 4 ); + var element = $( "#spin" ).val( 0 ).spinner(); + equal( element.spinner( "widget" ).find( ".ui-icon:first" ).attr( "class" ), "ui-icon ui-icon-triangle-1-n" ); + equal( element.spinner( "widget" ).find( ".ui-icon:last" ).attr( "class" ), "ui-icon ui-icon-triangle-1-s" ); + + element.spinner( "option", "icons", { + up: "ui-icon-carat-1-n", + down: "ui-icon-carat-1-s" + }); + equal( element.spinner( "widget" ).find( ".ui-icon:first" ).attr( "class" ), "ui-icon ui-icon-carat-1-n" ); + equal( element.spinner( "widget" ).find( ".ui-icon:last" ).attr( "class" ), "ui-icon ui-icon-carat-1-s" ); +}); + +test( "icons: custom ", function() { + expect( 2 ); + var element = $( "#spin" ).val( 0 ).spinner({ + icons: { + down: "custom-down", + up: "custom-up" + } + }).spinner( "widget" ); + equal( element.find( ".ui-icon:first" ).attr( "class" ), "ui-icon custom-up" ); + equal( element.find( ".ui-icon:last" ).attr( "class" ), "ui-icon custom-down" ); +}); + +test( "incremental, false", function() { + expect( 100 ); + + var i, + prev = 0, + element = $( "#spin" ).val( prev ).spinner({ + incremental: false, + spin: function( event, ui ) { + equal( ui.value - prev, 1 ); + prev = ui.value; + } + }); + + for ( i = 0; i < 100; i++ ) { + element.simulate( "keydown", { keyCode: $.ui.keyCode.UP } ); + } + element.simulate( "keyup", { keyCode: $.ui.keyCode.UP } ); +}); + +test( "incremental, true", function() { + expect( 100 ); + + function fill( num, val ) { + return $.map( new Array( num ), function() { + return val; + }); + } + + var i, + prev = 0, + expected = [].concat( fill( 18, 1 ), fill( 37, 2 ), fill( 14, 3 ), + fill( 9, 4 ), fill( 6, 5 ), fill( 5, 6 ), fill ( 5, 7 ), + fill( 4, 8 ), fill( 2, 9 ) ), + element = $( "#spin" ).val( prev ).spinner({ + incremental: true, + spin: function( event, ui ) { + equal( ui.value - prev, expected[ i ] ); + prev = ui.value; + } + }); + + for ( i = 0; i < 100; i++ ) { + element.simulate( "keydown", { keyCode: $.ui.keyCode.UP } ); + } + element.simulate( "keyup", { keyCode: $.ui.keyCode.UP } ); +}); + +test( "incremental, function", function() { + expect( 100 ); + + var i, + prev = 0, + element = $( "#spin" ).val( prev ).spinner({ + incremental: function( i ) { + return i; + }, + spin: function( event, ui ) { + equal( ui.value - prev, i + 1 ); + prev = ui.value; + } + }); + + for ( i = 0; i < 100; i++ ) { + element.simulate( "keydown", { keyCode: $.ui.keyCode.UP } ); + } + element.simulate( "keyup", { keyCode: $.ui.keyCode.UP } ); +}); + +test( "numberFormat, number", function() { + expect( 2 ); + var element = $( "#spin" ).val( 0 ).spinner({ numberFormat: "n" }); + equal( element.val(), "0.00", "formatted on init" ); + element.spinner( "stepUp" ); + equal( element.val(), "1.00", "formatted after step" ); +}); + +test( "numberFormat, number, simple", function() { + expect( 2 ); + var element = $( "#spin" ).val( 0 ).spinner({ numberFormat: "n0" }); + equal( element.val(), "0", "formatted on init" ); + element.spinner( "stepUp" ); + equal( element.val(), "1", "formatted after step" ); +}); + +test( "numberFormat, currency", function() { + expect( 2 ); + var element = $( "#spin" ).val( 0 ).spinner({ numberFormat: "C" }); + equal( element.val(), "$0.00", "formatted on init" ); + element.spinner( "stepUp" ); + equal( element.val(), "$1.00", "formatted after step" ); +}); + +test( "numberFormat, change", function() { + expect( 2 ); + var element = $( "#spin" ).val( 5 ).spinner({ numberFormat: "n1" }); + equal( element.val(), "5.0", "formatted on init" ); + element.spinner( "option", "numberFormat", "c" ); + equal( element.val(), "$5.00", "formatted after change" ); +}); + +test( "culture, null", function() { + expect( 2 ); + Globalize.culture( "ja-JP" ); + var element = $( "#spin" ).val( 0 ).spinner({ numberFormat: "C" }); + equal( element.val(), "¥0", "formatted on init" ); + element.spinner( "stepUp" ); + equal( element.val(), "¥1", "formatted after step" ); + + // reset culture + Globalize.culture( "default" ); +}); + +test( "currency, ja-JP", function() { + expect( 2 ); + var element = $( "#spin" ).val( 0 ).spinner({ + numberFormat: "C", + culture: "ja-JP" + }); + equal( element.val(), "¥0", "formatted on init" ); + element.spinner( "stepUp" ); + equal( element.val(), "¥1", "formatted after step" ); +}); + +test( "currency, change", function() { + expect( 2 ); + var element = $( "#spin" ).val( 5 ).spinner({ + numberFormat: "C", + culture: "ja-JP" + }); + equal( element.val(), "¥5", "formatted on init" ); + element.spinner( "option", "culture", "en" ); + equal( element.val(), "$5.00", "formatted after change" ); +}); + +test( "max", function() { + expect( 3 ); + var element = $( "#spin" ).val( 1000 ).spinner({ max: 100 }); + equal( element.val(), 1000, "value not constrained on init" ); + + element.spinner( "value", 1000 ); + equal( element.val(), 100, "max constrained in value method" ); + + element.val( 1000 ).blur(); + equal( element.val(), 1000, "max not constrained if manual entry" ); +}); + +test( "max, string", function() { + expect( 3 ); + var element = $( "#spin" ) + .val( 1000 ) + .spinner({ + max: "$100.00", + numberFormat: "C", + culture: "en" + }); + equal( element.val(), "$1,000.00", "value not constrained on init" ); + equal( element.spinner( "option", "max" ), 100, "option converted to number" ); + + element.spinner( "value", 1000 ); + equal( element.val(), "$100.00", "max constrained in value method" ); +}); + +test( "min", function() { + expect( 3 ); + var element = $( "#spin" ).val( -1000 ).spinner({ min: -100 }); + equal( element.val(), -1000, "value not constrained on init" ); + + element.spinner( "value", -1000 ); + equal( element.val(), -100, "min constrained in value method" ); + + element.val( -1000 ).blur(); + equal( element.val(), -1000, "min not constrained if manual entry" ); +}); + +test( "min, string", function() { + expect( 3 ); + var element = $( "#spin" ) + .val( -1000 ) + .spinner({ + min: "-$100.00", + numberFormat: "C", + culture: "en" + }); + equal( element.val(), "($1,000.00)", "value not constrained on init" ); + equal( element.spinner( "option", "min" ), -100, "option converted to number" ); + + element.spinner( "value", -1000 ); + equal( element.val(), "($100.00)", "min constrained in value method" ); +}); + +test( "step, 2", function() { + expect( 3 ); + var element = $( "#spin" ).val( 0 ).spinner({ step: 2 }); + + element.spinner( "stepUp" ); + equal( element.val(), "2", "stepUp" ); + + element.spinner( "value", "10.5" ); + equal( element.val(), "10", "value reset to 10" ); + + element.val( "4.5" ); + element.spinner( "stepUp" ); + equal( element.val(), "6", "stepUp" ); +}); + +test( "step, 0.7", function() { + expect( 1 ); + var element = $("#spin").val( 0 ).spinner({ + step: 0.7 + }); + + element.spinner( "stepUp" ); + equal( element.val(), "0.7", "stepUp" ); +}); + +test( "step, string", function() { + expect( 2 ); + var element = $("#spin").val( 0 ).spinner({ + step: "$0.70", + numberFormat: "C", + culture: "en" + }); + + equal( element.spinner( "option", "step" ), 0.7, "option converted to number" ); + + element.spinner( "stepUp" ); + equal( element.val(), "$0.70", "stepUp" ); +}); + +})( jQuery ); diff --git a/apps/it/static/js/ui/tests/unit/spinner/spinner_test_helpers.js b/apps/it/static/js/ui/tests/unit/spinner/spinner_test_helpers.js new file mode 100644 index 0000000..2021e8f --- /dev/null +++ b/apps/it/static/js/ui/tests/unit/spinner/spinner_test_helpers.js @@ -0,0 +1,8 @@ +TestHelpers.spinner = { + simulateKeyDownUp: function( element, keyCode, shift ) { + element + .simulate( "keydown", { keyCode: keyCode, shiftKey: shift || false } ) + .simulate( "keyup", { keyCode: keyCode, shiftKey: shift || false } ); + } +}; + diff --git a/apps/it/static/js/ui/tests/unit/subsuite.js b/apps/it/static/js/ui/tests/unit/subsuite.js new file mode 100644 index 0000000..c81b20f --- /dev/null +++ b/apps/it/static/js/ui/tests/unit/subsuite.js @@ -0,0 +1,25 @@ +(function() { + +var versions = [ + "1.6", "1.6.1", "1.6.2", "1.6.3", "1.6.4", + "1.7", "1.7.1", "1.7.2", + "1.8.0", "1.8.1", "1.8.2", "1.8.3", + "1.9.0", "1.9.1", + "1.10.0", "1.10.1", "1.10.2", + "git" + ], + additionalTests = { + // component: [ "other_test.html" ] + }; + +window.testAllVersions = function( widget ) { + QUnit.testSuites( $.map( + [ widget + ".html" ].concat( additionalTests[ widget ] || [] ), + function( test ) { + return $.map( versions, function( version ) { + return test + "?jquery=" + version; + }); + })); +}; + +}()); diff --git a/apps/it/static/js/ui/tests/unit/swarminject.js b/apps/it/static/js/ui/tests/unit/swarminject.js new file mode 100644 index 0000000..ebd3ccf --- /dev/null +++ b/apps/it/static/js/ui/tests/unit/swarminject.js @@ -0,0 +1,10 @@ +// load testswarm agent +(function() { + var url = window.location.search; + url = decodeURIComponent( url.slice( url.indexOf("swarmURL=") + 9 ) ); + if ( !url || url.indexOf("http") !== 0 ) { + return; + } + document.write( "<scr" + "ipt src='http://swarm.jquery.org/js/inject.js?" + + (new Date()).getTime() + "'></scr" + "ipt>" ); +})(); diff --git a/apps/it/static/js/ui/tests/unit/tabs/all.html b/apps/it/static/js/ui/tests/unit/tabs/all.html new file mode 100644 index 0000000..355a05d --- /dev/null +++ b/apps/it/static/js/ui/tests/unit/tabs/all.html @@ -0,0 +1,30 @@ +<!doctype html> +<html lang="en"> +<head> + <meta charset="utf-8"> + <title>jQuery UI Tabs Test Suite</title> + + <script src="../../../jquery-1.10.2.js"></script> + + <link rel="stylesheet" href="../../../external/qunit.css"> + <link rel="stylesheet" href="../qunit-composite.css"> + <script src="../../../external/qunit.js"></script> + <script src="../qunit-composite.js"></script> + <script src="../subsuite.js"></script> + + <script> + testAllVersions( "tabs" ); + </script> +</head> +<body> + +<h1 id="qunit-header">jQuery UI Tabs Test Suite</h1> +<h2 id="qunit-banner"></h2> +<div id="qunit-testrunner-toolbar"></div> +<h2 id="qunit-userAgent"></h2> +<ol id="qunit-tests"></ol> +<div id="qunit-fixture"> + +</div> +</body> +</html> diff --git a/apps/it/static/js/ui/tests/unit/tabs/data/test.html b/apps/it/static/js/ui/tests/unit/tabs/data/test.html new file mode 100644 index 0000000..cd59e64 --- /dev/null +++ b/apps/it/static/js/ui/tests/unit/tabs/data/test.html @@ -0,0 +1 @@ +<p>…content loaded via Ajax.</p>
\ No newline at end of file diff --git a/apps/it/static/js/ui/tests/unit/tabs/tabs.html b/apps/it/static/js/ui/tests/unit/tabs/tabs.html new file mode 100644 index 0000000..7c79823 --- /dev/null +++ b/apps/it/static/js/ui/tests/unit/tabs/tabs.html @@ -0,0 +1,155 @@ +<!doctype html> +<html lang="en"> +<head> + <meta charset="utf-8"> + <title>jQuery UI Tabs Test Suite</title> + + <script src="../../jquery.js"></script> + <link rel="stylesheet" href="../../../external/qunit.css"> + <script src="../../../external/qunit.js"></script> + <script src="../../jquery.simulate.js"></script> + <script src="../testsuite.js"></script> + <script> + TestHelpers.loadResources({ + css: [ "ui.core", "ui.tabs" ], + js: [ + "ui/jquery.ui.core.js", + "ui/jquery.ui.widget.js", + "ui/jquery.ui.tabs.js" + ] + }); + </script> + + <script src="tabs_test_helpers.js"></script> + <script src="tabs_common.js"></script> + <script src="tabs_core.js"></script> + <script src="tabs_events.js"></script> + <script src="tabs_methods.js"></script> + <script src="tabs_options.js"></script> + + <script src="../swarminject.js"></script> + <style> + #tabs8, #tabs8 * { + margin: 0; + padding: 0; + font-size: 12px; + line-height: 15px; + } + </style> +</head> +<body> + +<h1 id="qunit-header">jQuery UI Tabs Test Suite</h1> +<h2 id="qunit-banner"></h2> +<div id="qunit-testrunner-toolbar"></div> +<h2 id="qunit-userAgent"></h2> +<ol id="qunit-tests"></ol> +<div id="qunit-fixture"> + +<div id="tabs1"> + <ul> + <li><a href="#fragment-1"><span>1</span></a></li> + <li><a href="#fragment-2"><span>2</span></a></li> + <li><a href="#fragment-3"><span>3</span></a></li> + </ul> + <div id="fragment-1"></div> + <div id="fragment-2"></div> + <div id="fragment-3"></div> +</div> + +<div id="tabs2"> + <ul> + <li><a href="#colon:test"><span>1</span></a></li> + <li><a href="#inline-style"><span>2</span></a></li> + <li><a href="data/test.html#test"><span>3</span></a></li> + <li aria-controls="custom-id"><a href="data/test.html"><span>4</span></a></li> + <li><a href="data/test.html" title="∫ßáö Սե"><span>5</span></a></li> + </ul> + <div id="colon:test"></div> + <div style="height: 300px;" id="inline-style"></div> + <div id="custom-id"></div> +</div> + +<div id="tabs3"> + <div> + <ul id="tabs3-list"> + <li><a href="#tabs3-1">1</a></li> + </ul> + </div> +</div> + +<div id="tabs4"> + <ul id="tabs4-list"> + <li><a href="#tabs4-1">1</a></li> + </ul> + <ol> + <li><a href="#tabs4-1">1</a></li> + </ol> +</div> + +<div id="tabs4a"> + <ol id="tabs4a-list"> + <li><a href="#tabs4a-1">1</a></li> + </ol> + <ul> + <li><a href="#tabs4a-1">1</a></li> + </ul> +</div> + +<div id="tabs5"> + <div> + <ul id="tabs5-list"></ul> + </div> +</div> + +<div id="tabs6"> + <ul id="tabs6-list"> + <li><a href="#tabs6-1">1</a> + <ul> + <li><a href="#item6-3">3</a></li> + <li><a href="#item6-4">4</a></li> + </ul> + </li> + <li><a href="#tabs6-2">2</a></li> + </ul> + <div id="tabs6-1"></div> + <div id="tabs6-2"></div> +</div> + +<div id="tabs7"> + <ul id="tabs7-list"> + <li><a href="#tabs7-1">1</a></li> + <li><a href="#tabs7-2">2</a></li> + </ul> + <div id="tabs7-2"></div> + <div id="tabs7-1"></div> +</div> + +<div id="tabs8Wrapper"> + <div id="tabs8"> + <ul id="tabs8-list"> + <li><a href="#tabs8-1">1</a></li> + <li><a href="#tabs8-2">2</a></li> + </ul> + <div id="tabs8-1"> + <p>Lorem ipsum</p> + <p>Lorem ipsum</p> + <p>Lorem ipsum</p> + </div> + <div id="tabs8-2"> + <p>Lorem ipsum</p> + </div> + </div> +</div> + +<div id="tabs9"> + <ul> + <li>not a tab</li> + <li><a href="#tabs9-1">tab</a></li> + </ul> + <div id="tabs9-1"></div> +</div> + +</div> +</body> +</html> diff --git a/apps/it/static/js/ui/tests/unit/tabs/tabs_common.js b/apps/it/static/js/ui/tests/unit/tabs/tabs_common.js new file mode 100644 index 0000000..a589515 --- /dev/null +++ b/apps/it/static/js/ui/tests/unit/tabs/tabs_common.js @@ -0,0 +1,18 @@ +TestHelpers.commonWidgetTests( "tabs", { + defaults: { + active: null, + collapsible: false, + disabled: false, + event: "click", + heightStyle: "content", + hide: null, + show: null, + + // callbacks + activate: null, + beforeActivate: null, + beforeLoad: null, + create: null, + load: null + } +}); diff --git a/apps/it/static/js/ui/tests/unit/tabs/tabs_core.js b/apps/it/static/js/ui/tests/unit/tabs/tabs_core.js new file mode 100644 index 0000000..5f70206 --- /dev/null +++ b/apps/it/static/js/ui/tests/unit/tabs/tabs_core.js @@ -0,0 +1,615 @@ +(function( $ ) { + +var state = TestHelpers.tabs.state; + +module( "tabs: core" ); + +test( "markup structure", function() { + expect( 3 ); + var element = $( "#tabs1" ).tabs(); + ok( element.hasClass( "ui-tabs" ), "main element is .ui-tabs" ); + ok( element.find( "ul" ).hasClass( "ui-tabs-nav" ), "list item is .ui-tabs-nav" ); + equal( element.find( ".ui-tabs-panel" ).length, 3, + ".ui-tabs-panel elements exist, correct number" ); +}); + +$.each({ + "deep ul": "#tabs3", + "multiple lists, ul first": "#tabs4", + "multiple lists, ol first": "#tabs5", + "empty list": "#tabs6" +}, function( type, selector ) { + test( "markup structure: " + type, function() { + expect( 2 ); + var element = $( selector ).tabs(); + ok( element.hasClass( "ui-tabs" ), "main element is .ui-tabs" ); + ok( $( selector + "-list" ).hasClass( "ui-tabs-nav" ), + "list item is .ui-tabs-nav" ); + }); +}); + +// #5893 - Sublist in the tab list are considered as tab +test( "nested list", function() { + expect( 1 ); + + var element = $( "#tabs6" ).tabs(); + equal( element.data( "ui-tabs" ).anchors.length, 2, "should contain 2 tab" ); +}); + +test( "disconnected from DOM", function() { + expect( 2 ); + + var element = $( "#tabs1" ).remove().tabs(); + equal( element.find( ".ui-tabs-nav" ).length, 1, "should initialize nav" ); + equal( element.find( ".ui-tabs-panel" ).length, 3, "should initialize panels" ); +}); + +test( "non-tab list items", function() { + expect( 2 ); + + var element = $( "#tabs9" ).tabs(); + equal( element.tabs( "option", "active" ), 0, "defaults to first tab" ); + equal( element.find( ".ui-tabs-nav li.ui-state-active" ).index(), 1, + "first actual tab is active" ); +}); + +test( "aria-controls", function() { + expect( 7 ); + var element = $( "#tabs1" ).tabs(), + tabs = element.find( ".ui-tabs-nav li" ); + tabs.each(function() { + var tab = $( this ), + anchor = tab.find( ".ui-tabs-anchor" ); + equal( anchor.prop( "hash" ).substring( 1 ), tab.attr( "aria-controls" ) ); + }); + + element = $( "#tabs2" ).tabs(); + tabs = element.find( ".ui-tabs-nav li" ); + equal( tabs.eq( 0 ).attr( "aria-controls" ), "colon:test" ); + equal( tabs.eq( 1 ).attr( "aria-controls" ), "inline-style" ); + ok( /^ui-tabs-\d+$/.test( tabs.eq( 2 ).attr( "aria-controls" ) ), "generated id" ); + equal( tabs.eq( 3 ).attr( "aria-controls" ), "custom-id" ); +}); + +test( "accessibility", function() { + expect( 49 ); + var element = $( "#tabs1" ).tabs({ + active: 1, + disabled: [ 2 ] + }), + tabs = element.find( ".ui-tabs-nav li" ), + anchors = tabs.find( ".ui-tabs-anchor" ), + panels = element.find( ".ui-tabs-panel" ); + + equal( element.find( ".ui-tabs-nav" ).attr( "role" ), "tablist", "tablist role" ); + tabs.each(function( index ) { + var tab = tabs.eq( index ), + anchor = anchors.eq( index ), + anchorId = anchor.attr( "id" ), + panel = panels.eq( index ); + equal( tab.attr( "role" ), "tab", "tab " + index + " role" ); + equal( tab.attr( "aria-labelledby" ), anchorId, "tab " + index + " aria-labelledby" ); + equal( anchor.attr( "role" ), "presentation", "anchor " + index + " role" ); + equal( anchor.attr( "tabindex" ), -1, "anchor " + index + " tabindex" ); + equal( panel.attr( "role" ), "tabpanel", "panel " + index + " role" ); + equal( panel.attr( "aria-labelledby" ), anchorId, "panel " + index + " aria-labelledby" ); + }); + + equal( tabs.eq( 1 ).attr( "aria-selected" ), "true", "active tab has aria-selected=true" ); + equal( tabs.eq( 1 ).attr( "tabindex" ), 0, "active tab has tabindex=0" ); + equal( tabs.eq( 1 ).attr( "aria-disabled" ), null, "enabled tab does not have aria-disabled" ); + equal( panels.eq( 1 ).attr( "aria-expanded" ), "true", "active panel has aria-expanded=true" ); + equal( panels.eq( 1 ).attr( "aria-hidden" ), "false", "active panel has aria-hidden=false" ); + equal( tabs.eq( 0 ).attr( "aria-selected" ), "false", "inactive tab has aria-selected=false" ); + equal( tabs.eq( 0 ).attr( "tabindex" ), -1, "inactive tab has tabindex=-1" ); + equal( tabs.eq( 0 ).attr( "aria-disabled" ), null, "enabled tab does not have aria-disabled" ); + equal( panels.eq( 0 ).attr( "aria-expanded" ), "false", "inactive panel has aria-expanded=false" ); + equal( panels.eq( 0 ).attr( "aria-hidden" ), "true", "inactive panel has aria-hidden=true" ); + equal( tabs.eq( 2 ).attr( "aria-selected" ), "false", "inactive tab has aria-selected=false" ); + equal( tabs.eq( 2 ).attr( "tabindex" ), -1, "inactive tab has tabindex=-1" ); + equal( tabs.eq( 2 ).attr( "aria-disabled" ), "true", "disabled tab has aria-disabled=true" ); + equal( panels.eq( 2 ).attr( "aria-expanded" ), "false", "inactive panel has aria-expanded=false" ); + equal( panels.eq( 2 ).attr( "aria-hidden" ), "true", "inactive panel has aria-hidden=true" ); + + element.tabs( "option", "active", 0 ); + equal( tabs.eq( 0 ).attr( "aria-selected" ), "true", "active tab has aria-selected=true" ); + equal( tabs.eq( 0 ).attr( "tabindex" ), 0, "active tab has tabindex=0" ); + equal( tabs.eq( 0 ).attr( "aria-disabled" ), null, "enabled tab does not have aria-disabled" ); + equal( panels.eq( 0 ).attr( "aria-expanded" ), "true", "active panel has aria-expanded=true" ); + equal( panels.eq( 0 ).attr( "aria-hidden" ), "false", "active panel has aria-hidden=false" ); + equal( tabs.eq( 1 ).attr( "aria-selected" ), "false", "inactive tab has aria-selected=false" ); + equal( tabs.eq( 1 ).attr( "tabindex" ), -1, "inactive tab has tabindex=-1" ); + equal( tabs.eq( 1 ).attr( "aria-disabled" ), null, "enabled tab does not have aria-disabled" ); + equal( panels.eq( 1 ).attr( "aria-expanded" ), "false", "inactive panel has aria-expanded=false" ); + equal( panels.eq( 1 ).attr( "aria-hidden" ), "true", "inactive panel has aria-hidden=true" ); + equal( tabs.eq( 2 ).attr( "aria-selected" ), "false", "inactive tab has aria-selected=false" ); + equal( tabs.eq( 2 ).attr( "tabindex" ), -1, "inactive tab has tabindex=-1" ); + equal( tabs.eq( 2 ).attr( "aria-disabled" ), "true", "disabled tab has aria-disabled=true" ); + equal( panels.eq( 2 ).attr( "aria-expanded" ), "false", "inactive panel has aria-expanded=false" ); + equal( panels.eq( 2 ).attr( "aria-hidden" ), "true", "inactive panel has aria-hidden=true" ); +}); + +asyncTest( "accessibility - ajax", function() { + expect( 4 ); + var element = $( "#tabs2" ).tabs(), + panel = $( "#custom-id" ); + + equal( panel.attr( "aria-live" ), "polite", "remote panel has aria-live" ); + equal( panel.attr( "aria-busy" ), null, "does not have aria-busy on init" ); + element.tabs( "option", "active", 3 ); + equal( panel.attr( "aria-busy" ), "true", "panel has aria-busy during load" ); + element.one( "tabsload", function() { + setTimeout(function() { + equal( panel.attr( "aria-busy" ), null, "panel does not have aria-busy after load" ); + start(); + }, 1 ); + }); +}); + +asyncTest( "keyboard support - LEFT, RIGHT, UP, DOWN, HOME, END, SPACE, ENTER", function() { + expect( 92 ); + var element = $( "#tabs1" ).tabs({ + collapsible: true + }), + tabs = element.find( ".ui-tabs-nav li" ), + panels = element.find( ".ui-tabs-panel" ), + keyCode = $.ui.keyCode; + + element.data( "ui-tabs" ).delay = 50; + + equal( tabs.filter( ".ui-state-focus" ).length, 0, "no tabs focused on init" ); + tabs.eq( 0 ).simulate( "focus" ); + + // down, right, down (wrap), up (wrap) + function step1() { + ok( tabs.eq( 0 ).is( ".ui-state-focus" ), "first tab has focus" ); + equal( tabs.eq( 0 ).attr( "aria-selected" ), "true", "first tab has aria-selected=true" ); + ok( panels.eq( 0 ).is( ":visible" ), "first panel is visible" ); + + tabs.eq( 0 ).simulate( "keydown", { keyCode: keyCode.DOWN } ); + ok( tabs.eq( 1 ).is( ".ui-state-focus" ), "DOWN moves focus to next tab" ); + ok( !tabs.eq( 0 ).is( ".ui-state-focus" ), "first tab is no longer focused" ); + equal( tabs.eq( 1 ).attr( "aria-selected" ), "true", "second tab has aria-selected=true" ); + equal( tabs.eq( 0 ).attr( "aria-selected" ), "false", "first tab has aria-selected=false" ); + ok( panels.eq( 1 ).is( ":hidden" ), "second panel is still hidden" ); + equal( panels.eq( 1 ).attr( "aria-expanded" ), "false", "second panel has aria-expanded=false" ); + equal( panels.eq( 1 ).attr( "aria-hidden" ), "true", "second panel has aria-hidden=true" ); + ok( panels.eq( 0 ).is( ":visible" ), "first panel is still visible" ); + equal( panels.eq( 0 ).attr( "aria-expanded" ), "true", "first panel has aria-expanded=true" ); + equal( panels.eq( 0 ).attr( "aria-hidden" ), "false", "first panel has aria-hidden=false" ); + + tabs.eq( 1 ).simulate( "keydown", { keyCode: keyCode.RIGHT } ); + ok( tabs.eq( 2 ).is( ".ui-state-focus" ), "RIGHT moves focus to next tab" ); + equal( tabs.eq( 2 ).attr( "aria-selected" ), "true", "third tab has aria-selected=true" ); + equal( tabs.eq( 1 ).attr( "aria-selected" ), "false", "second tab has aria-selected=false" ); + ok( panels.eq( 2 ).is( ":hidden" ), "third panel is still hidden" ); + equal( panels.eq( 2 ).attr( "aria-expanded" ), "false", "third panel has aria-expanded=false" ); + equal( panels.eq( 2 ).attr( "aria-hidden" ), "true", "third panel has aria-hidden=true" ); + ok( panels.eq( 0 ).is( ":visible" ), "first panel is still visible" ); + equal( panels.eq( 0 ).attr( "aria-expanded" ), "true", "first panel has aria-expanded=true" ); + equal( panels.eq( 0 ).attr( "aria-hidden" ), "false", "first panel has aria-hidden=false" ); + + tabs.eq( 2 ).simulate( "keydown", { keyCode: keyCode.DOWN } ); + ok( tabs.eq( 0 ).is( ".ui-state-focus" ), "DOWN wraps focus to first tab" ); + equal( tabs.eq( 0 ).attr( "aria-selected" ), "true", "first tab has aria-selected=true" ); + equal( tabs.eq( 2 ).attr( "aria-selected" ), "false", "third tab has aria-selected=false" ); + ok( panels.eq( 0 ).is( ":visible" ), "first panel is still visible" ); + equal( panels.eq( 0 ).attr( "aria-expanded" ), "true", "first panel has aria-expanded=true" ); + equal( panels.eq( 0 ).attr( "aria-hidden" ), "false", "first panel has aria-hidden=false" ); + + tabs.eq( 0 ).simulate( "keydown", { keyCode: keyCode.UP } ); + ok( tabs.eq( 2 ).is( ".ui-state-focus" ), "UP wraps focus to last tab" ); + equal( tabs.eq( 2 ).attr( "aria-selected" ), "true", "third tab has aria-selected=true" ); + equal( tabs.eq( 0 ).attr( "aria-selected" ), "false", "first tab has aria-selected=false" ); + ok( panels.eq( 2 ).is( ":hidden" ), "third panel is still hidden" ); + equal( panels.eq( 2 ).attr( "aria-expanded" ), "false", "third panel has aria-expanded=false" ); + equal( panels.eq( 2 ).attr( "aria-hidden" ), "true", "third panel has aria-hidden=true" ); + ok( panels.eq( 0 ).is( ":visible" ), "first panel is still visible" ); + equal( panels.eq( 0 ).attr( "aria-expanded" ), "true", "first panel has aria-expanded=true" ); + equal( panels.eq( 0 ).attr( "aria-hidden" ), "false", "first panel has aria-hidden=false" ); + + setTimeout( step2, 100 ); + } + + // left, home, space + function step2() { + equal( tabs.eq( 2 ).attr( "aria-selected" ), "true", "third tab has aria-selected=true" ); + equal( tabs.eq( 0 ).attr( "aria-selected" ), "false", "first tab has aria-selected=false" ); + ok( panels.eq( 2 ).is( ":visible" ), "third panel is visible" ); + equal( panels.eq( 2 ).attr( "aria-expanded" ), "true", "third panel has aria-expanded=true" ); + equal( panels.eq( 2 ).attr( "aria-hidden" ), "false", "third panel has aria-hidden=false" ); + ok( panels.eq( 0 ).is( ":hidden" ), "first panel is hidden" ); + equal( panels.eq( 0 ).attr( "aria-expanded" ), "false", "first panel has aria-expanded=false" ); + equal( panels.eq( 0 ).attr( "aria-hidden" ), "true", "first panel has aria-hidden=true" ); + + tabs.eq( 2 ).simulate( "keydown", { keyCode: keyCode.LEFT } ); + ok( tabs.eq( 1 ).is( ".ui-state-focus" ), "LEFT moves focus to previous tab" ); + equal( tabs.eq( 1 ).attr( "aria-selected" ), "true", "second tab has aria-selected=true" ); + equal( tabs.eq( 2 ).attr( "aria-selected" ), "false", "third tab has aria-selected=false" ); + ok( panels.eq( 1 ).is( ":hidden" ), "second panel is still hidden" ); + equal( panels.eq( 1 ).attr( "aria-expanded" ), "false", "second panel has aria-expanded=false" ); + equal( panels.eq( 1 ).attr( "aria-hidden" ), "true", "second panel has aria-hidden=true" ); + ok( panels.eq( 2 ).is( ":visible" ), "third panel is still visible" ); + equal( panels.eq( 2 ).attr( "aria-expanded" ), "true", "third panel has aria-expanded=true" ); + equal( panels.eq( 2 ).attr( "aria-hidden" ), "false", "third panel has aria-hidden=false" ); + + tabs.eq( 1 ).simulate( "keydown", { keyCode: keyCode.HOME } ); + ok( tabs.eq( 0 ).is( ".ui-state-focus" ), "HOME moves focus to first tab" ); + equal( tabs.eq( 0 ).attr( "aria-selected" ), "true", "first tab has aria-selected=true" ); + equal( tabs.eq( 1 ).attr( "aria-selected" ), "false", "second tab has aria-selected=false" ); + ok( panels.eq( 0 ).is( ":hidden" ), "first panel is still hidden" ); + equal( panels.eq( 0 ).attr( "aria-expanded" ), "false", "first panel has aria-expanded=false" ); + equal( panels.eq( 0 ).attr( "aria-hidden" ), "true", "first panel has aria-hidden=true" ); + ok( panels.eq( 2 ).is( ":visible" ), "third panel is still visible" ); + equal( panels.eq( 2 ).attr( "aria-expanded" ), "true", "third panel has aria-expanded=true" ); + equal( panels.eq( 2 ).attr( "aria-hidden" ), "false", "third panel has aria-hidden=false" ); + + // SPACE activates, cancels delay + tabs.eq( 0 ).simulate( "keydown", { keyCode: keyCode.SPACE } ); + setTimeout( step3, 1 ); + } + + // end, enter + function step3() { + equal( tabs.eq( 0 ).attr( "aria-selected" ), "true", "first tab has aria-selected=true" ); + equal( tabs.eq( 2 ).attr( "aria-selected" ), "false", "third tab has aria-selected=false" ); + ok( panels.eq( 0 ).is( ":visible" ), "first panel is visible" ); + equal( panels.eq( 0 ).attr( "aria-expanded" ), "true", "first panel has aria-expanded=true" ); + equal( panels.eq( 0 ).attr( "aria-hidden" ), "false", "first panel has aria-hidden=false" ); + ok( panels.eq( 2 ).is( ":hidden" ), "third panel is hidden" ); + equal( panels.eq( 2 ).attr( "aria-expanded" ), "false", "third panel has aria-expanded=false" ); + equal( panels.eq( 2 ).attr( "aria-hidden" ), "true", "third panel has aria-hidden=true" ); + + tabs.eq( 0 ).simulate( "keydown", { keyCode: keyCode.END } ); + ok( tabs.eq( 2 ).is( ".ui-state-focus" ), "END moves focus to last tab" ); + equal( tabs.eq( 2 ).attr( "aria-selected" ), "true", "third tab has aria-selected=true" ); + equal( tabs.eq( 0 ).attr( "aria-selected" ), "false", "first tab has aria-selected=false" ); + ok( panels.eq( 2 ).is( ":hidden" ), "third panel is still hidden" ); + equal( panels.eq( 2 ).attr( "aria-expanded" ), "false", "third panel has aria-expanded=false" ); + equal( panels.eq( 2 ).attr( "aria-hidden" ), "true", "third panel has aria-hidden=true" ); + ok( panels.eq( 0 ).is( ":visible" ), "first panel is still visible" ); + equal( panels.eq( 0 ).attr( "aria-expanded" ), "true", "first panel has aria-expanded=true" ); + equal( panels.eq( 0 ).attr( "aria-hidden" ), "false", "first panel has aria-hidden=false" ); + + // ENTER activates, cancels delay + tabs.eq( 0 ).simulate( "keydown", { keyCode: keyCode.ENTER } ); + setTimeout( step4, 1 ); + } + + // enter (collapse) + function step4() { + equal( tabs.eq( 2 ).attr( "aria-selected" ), "true", "third tab has aria-selected=true" ); + ok( panels.eq( 2 ).is( ":visible" ), "third panel is visible" ); + equal( panels.eq( 2 ).attr( "aria-expanded" ), "true", "third panel has aria-expanded=true" ); + equal( panels.eq( 2 ).attr( "aria-hidden" ), "false", "third panel has aria-hidden=false" ); + ok( panels.eq( 0 ).is( ":hidden" ), "first panel is hidden" ); + equal( panels.eq( 0 ).attr( "aria-expanded" ), "false", "first panel has aria-expanded=false" ); + equal( panels.eq( 0 ).attr( "aria-hidden" ), "true", "first panel has aria-hidden=true" ); + + // ENTER collapses if active + tabs.eq( 2 ).simulate( "keydown", { keyCode: keyCode.ENTER } ); + equal( tabs.eq( 2 ).attr( "aria-selected" ), "false", "third tab has aria-selected=false" ); + ok( panels.eq( 2 ).is( ":hidden" ), "third panel is hidden" ); + equal( panels.eq( 2 ).attr( "aria-expanded" ), "false", "third panel has aria-expanded=false" ); + equal( panels.eq( 2 ).attr( "aria-hidden" ), "true", "third panel has aria-hidden=true" ); + + setTimeout( start, 1 ); + } + + setTimeout( step1, 1 ); +}); + +asyncTest( "keyboard support - CTRL navigation", function() { + expect( 115 ); + var element = $( "#tabs1" ).tabs(), + tabs = element.find( ".ui-tabs-nav li" ), + panels = element.find( ".ui-tabs-panel" ), + keyCode = $.ui.keyCode; + + element.data( "ui-tabs" ).delay = 50; + + equal( tabs.filter( ".ui-state-focus" ).length, 0, "no tabs focused on init" ); + tabs.eq( 0 ).simulate( "focus" ); + + // down + function step1() { + ok( tabs.eq( 0 ).is( ".ui-state-focus" ), "first tab has focus" ); + equal( tabs.eq( 0 ).attr( "aria-selected" ), "true", "first tab has aria-selected=true" ); + ok( panels.eq( 0 ).is( ":visible" ), "first panel is visible" ); + + tabs.eq( 0 ).simulate( "keydown", { keyCode: keyCode.DOWN, ctrlKey: true } ); + ok( tabs.eq( 1 ).is( ".ui-state-focus" ), "DOWN moves focus to next tab" ); + ok( !tabs.eq( 0 ).is( ".ui-state-focus" ), "first tab is no longer focused" ); + equal( tabs.eq( 1 ).attr( "aria-selected" ), "false", "second tab has aria-selected=false" ); + equal( tabs.eq( 0 ).attr( "aria-selected" ), "true", "first tab has aria-selected=true" ); + ok( panels.eq( 1 ).is( ":hidden" ), "second panel is still hidden" ); + equal( panels.eq( 1 ).attr( "aria-expanded" ), "false", "second panel has aria-expanded=false" ); + equal( panels.eq( 1 ).attr( "aria-hidden" ), "true", "second panel has aria-hidden=true" ); + ok( panels.eq( 0 ).is( ":visible" ), "first panel is still visible" ); + equal( panels.eq( 0 ).attr( "aria-expanded" ), "true", "first panel has aria-expanded=true" ); + equal( panels.eq( 0 ).attr( "aria-hidden" ), "false", "first panel has aria-hidden=false" ); + + setTimeout( step2, 100 ); + } + + // right + function step2() { + equal( tabs.eq( 0 ).attr( "aria-selected" ), "true", "first tab has aria-selected=true" ); + ok( panels.eq( 0 ).is( ":visible" ), "first panel is visible" ); + equal( panels.eq( 0 ).attr( "aria-expanded" ), "true", "first panel has aria-expanded=true" ); + equal( panels.eq( 0 ).attr( "aria-hidden" ), "false", "first panel has aria-hidden=false" ); + ok( panels.eq( 1 ).is( ":hidden" ), "second panel is hidden" ); + equal( panels.eq( 1 ).attr( "aria-expanded" ), "false", "second panel has aria-expanded=false" ); + equal( panels.eq( 1 ).attr( "aria-hidden" ), "true", "second panel has aria-hidden=true" ); + + tabs.eq( 1 ).simulate( "keydown", { keyCode: keyCode.RIGHT, ctrlKey: true } ); + ok( tabs.eq( 2 ).is( ".ui-state-focus" ), "RIGHT moves focus to next tab" ); + equal( tabs.eq( 2 ).attr( "aria-selected" ), "false", "third tab has aria-selected=false" ); + equal( tabs.eq( 0 ).attr( "aria-selected" ), "true", "first tab has aria-selected=true" ); + ok( panels.eq( 2 ).is( ":hidden" ), "third panel is still hidden" ); + equal( panels.eq( 2 ).attr( "aria-expanded" ), "false", "third panel has aria-expanded=false" ); + equal( panels.eq( 2 ).attr( "aria-hidden" ), "true", "third panel has aria-hidden=true" ); + ok( panels.eq( 0 ).is( ":visible" ), "first panel is still visible" ); + equal( panels.eq( 0 ).attr( "aria-expanded" ), "true", "first panel has aria-expanded=true" ); + equal( panels.eq( 0 ).attr( "aria-hidden" ), "false", "first panel has aria-hidden=false" ); + + setTimeout( step3, 100 ); + } + + // down (wrap) + function step3() { + equal( tabs.eq( 0 ).attr( "aria-selected" ), "true", "first tab has aria-selected=true" ); + ok( panels.eq( 0 ).is( ":visible" ), "first panel is visible" ); + equal( panels.eq( 0 ).attr( "aria-expanded" ), "true", "first panel has aria-expanded=true" ); + equal( panels.eq( 0 ).attr( "aria-hidden" ), "false", "first panel has aria-hidden=false" ); + ok( panels.eq( 2 ).is( ":hidden" ), "third panel is hidden" ); + equal( panels.eq( 2 ).attr( "aria-expanded" ), "false", "third panel has aria-expanded=false" ); + equal( panels.eq( 2 ).attr( "aria-hidden" ), "true", "third panel has aria-hidden=true" ); + + tabs.eq( 2 ).simulate( "keydown", { keyCode: keyCode.DOWN, ctrlKey: true } ); + ok( tabs.eq( 0 ).is( ".ui-state-focus" ), "DOWN wraps focus to first tab" ); + equal( tabs.eq( 0 ).attr( "aria-selected" ), "true", "first tab has aria-selected=true" ); + ok( panels.eq( 0 ).is( ":visible" ), "first panel is still visible" ); + equal( panels.eq( 0 ).attr( "aria-expanded" ), "true", "first panel has aria-expanded=true" ); + equal( panels.eq( 0 ).attr( "aria-hidden" ), "false", "first panel has aria-hidden=false" ); + + setTimeout( step4, 100 ); + } + + // up (wrap) + function step4() { + equal( tabs.eq( 0 ).attr( "aria-selected" ), "true", "first tab has aria-selected=true" ); + ok( panels.eq( 0 ).is( ":visible" ), "first panel is visible" ); + equal( panels.eq( 0 ).attr( "aria-expanded" ), "true", "first panel has aria-expanded=true" ); + equal( panels.eq( 0 ).attr( "aria-hidden" ), "false", "first panel has aria-hidden=false" ); + + tabs.eq( 0 ).simulate( "keydown", { keyCode: keyCode.UP, ctrlKey: true } ); + ok( tabs.eq( 2 ).is( ".ui-state-focus" ), "UP wraps focus to last tab" ); + equal( tabs.eq( 2 ).attr( "aria-selected" ), "false", "third tab has aria-selected=false" ); + equal( tabs.eq( 0 ).attr( "aria-selected" ), "true", "first tab has aria-selected=true" ); + ok( panels.eq( 2 ).is( ":hidden" ), "third panel is still hidden" ); + equal( panels.eq( 2 ).attr( "aria-expanded" ), "false", "third panel has aria-expanded=false" ); + equal( panels.eq( 2 ).attr( "aria-hidden" ), "true", "third panel has aria-hidden=true" ); + ok( panels.eq( 0 ).is( ":visible" ), "first panel is still visible" ); + equal( panels.eq( 0 ).attr( "aria-expanded" ), "true", "first panel has aria-expanded=true" ); + equal( panels.eq( 0 ).attr( "aria-hidden" ), "false", "first panel has aria-hidden=false" ); + + setTimeout( step5, 100 ); + } + + // left + function step5() { + equal( tabs.eq( 0 ).attr( "aria-selected" ), "true", "first tab has aria-selected=true" ); + ok( panels.eq( 0 ).is( ":visible" ), "first panel is visible" ); + equal( panels.eq( 0 ).attr( "aria-expanded" ), "true", "first panel has aria-expanded=true" ); + equal( panels.eq( 0 ).attr( "aria-hidden" ), "false", "first panel has aria-hidden=false" ); + ok( panels.eq( 2 ).is( ":hidden" ), "third panel is hidden" ); + equal( panels.eq( 2 ).attr( "aria-expanded" ), "false", "third panel has aria-expanded=false" ); + equal( panels.eq( 2 ).attr( "aria-hidden" ), "true", "third panel has aria-hidden=true" ); + + tabs.eq( 2 ).simulate( "keydown", { keyCode: keyCode.LEFT, ctrlKey: true } ); + ok( tabs.eq( 1 ).is( ".ui-state-focus" ), "LEFT moves focus to previous tab" ); + equal( tabs.eq( 1 ).attr( "aria-selected" ), "false", "second tab has aria-selected=false" ); + equal( tabs.eq( 0 ).attr( "aria-selected" ), "true", "first tab has aria-selected=true" ); + ok( panels.eq( 1 ).is( ":hidden" ), "second panel is still hidden" ); + equal( panels.eq( 1 ).attr( "aria-expanded" ), "false", "second panel has aria-expanded=false" ); + equal( panels.eq( 1 ).attr( "aria-hidden" ), "true", "second panel has aria-hidden=true" ); + ok( panels.eq( 0 ).is( ":visible" ), "first panel is still visible" ); + equal( panels.eq( 0 ).attr( "aria-expanded" ), "true", "first panel has aria-expanded=true" ); + equal( panels.eq( 0 ).attr( "aria-hidden" ), "false", "first panel has aria-hidden=false" ); + + setTimeout( step6, 100 ); + } + + // home + function step6() { + equal( tabs.eq( 0 ).attr( "aria-selected" ), "true", "first tab has aria-selected=true" ); + ok( panels.eq( 0 ).is( ":visible" ), "first panel is visible" ); + equal( panels.eq( 0 ).attr( "aria-expanded" ), "true", "first panel has aria-expanded=true" ); + equal( panels.eq( 0 ).attr( "aria-hidden" ), "false", "first panel has aria-hidden=false" ); + ok( panels.eq( 1 ).is( ":hidden" ), "second panel is hidden" ); + equal( panels.eq( 1 ).attr( "aria-expanded" ), "false", "second panel has aria-expanded=false" ); + equal( panels.eq( 1 ).attr( "aria-hidden" ), "true", "second panel has aria-hidden=true" ); + + tabs.eq( 1 ).simulate( "keydown", { keyCode: keyCode.HOME, ctrlKey: true } ); + ok( tabs.eq( 0 ).is( ".ui-state-focus" ), "HOME moves focus to first tab" ); + equal( tabs.eq( 0 ).attr( "aria-selected" ), "true", "first tab has aria-selected=true" ); + equal( tabs.eq( 1 ).attr( "aria-selected" ), "false", "second tab has aria-selected=false" ); + ok( panels.eq( 1 ).is( ":hidden" ), "second panel is still hidden" ); + equal( panels.eq( 1 ).attr( "aria-expanded" ), "false", "second panel has aria-expanded=false" ); + equal( panels.eq( 1 ).attr( "aria-hidden" ), "true", "second panel has aria-hidden=true" ); + ok( panels.eq( 0 ).is( ":visible" ), "first panel is still visible" ); + equal( panels.eq( 0 ).attr( "aria-expanded" ), "true", "first panel has aria-expanded=true" ); + equal( panels.eq( 0 ).attr( "aria-hidden" ), "false", "first panel has aria-hidden=false" ); + + setTimeout( step7, 100 ); + } + + // end + function step7() { + equal( tabs.eq( 0 ).attr( "aria-selected" ), "true", "first tab has aria-selected=true" ); + ok( panels.eq( 0 ).is( ":visible" ), "first panel is visible" ); + equal( panels.eq( 0 ).attr( "aria-expanded" ), "true", "first panel has aria-expanded=true" ); + equal( panels.eq( 0 ).attr( "aria-hidden" ), "false", "first panel has aria-hidden=false" ); + + tabs.eq( 0 ).simulate( "keydown", { keyCode: keyCode.END, ctrlKey: true } ); + ok( tabs.eq( 2 ).is( ".ui-state-focus" ), "END moves focus to last tab" ); + equal( tabs.eq( 2 ).attr( "aria-selected" ), "false", "third tab has aria-selected=false" ); + equal( tabs.eq( 0 ).attr( "aria-selected" ), "true", "first tab has aria-selected=true" ); + ok( panels.eq( 2 ).is( ":hidden" ), "third panel is still hidden" ); + equal( panels.eq( 2 ).attr( "aria-expanded" ), "false", "third panel has aria-expanded=false" ); + equal( panels.eq( 2 ).attr( "aria-hidden" ), "true", "third panel has aria-hidden=true" ); + ok( panels.eq( 0 ).is( ":visible" ), "first panel is still visible" ); + equal( panels.eq( 0 ).attr( "aria-expanded" ), "true", "first panel has aria-expanded=true" ); + equal( panels.eq( 0 ).attr( "aria-hidden" ), "false", "first panel has aria-hidden=false" ); + + setTimeout( step8, 100 ); + } + + // space + function step8() { + equal( tabs.eq( 0 ).attr( "aria-selected" ), "true", "first tab has aria-selected=true" ); + ok( panels.eq( 0 ).is( ":visible" ), "first panel is visible" ); + equal( panels.eq( 0 ).attr( "aria-expanded" ), "true", "first panel has aria-expanded=true" ); + equal( panels.eq( 0 ).attr( "aria-hidden" ), "false", "first panel has aria-hidden=false" ); + ok( panels.eq( 2 ).is( ":hidden" ), "third panel is hidden" ); + equal( panels.eq( 2 ).attr( "aria-expanded" ), "false", "third panel has aria-expanded=false" ); + equal( panels.eq( 2 ).attr( "aria-hidden" ), "true", "third panel has aria-hidden=true" ); + + tabs.eq( 2 ).simulate( "keydown", { keyCode: keyCode.SPACE } ); + equal( tabs.eq( 2 ).attr( "aria-selected" ), "true", "third tab has aria-selected=true" ); + equal( tabs.eq( 0 ).attr( "aria-selected" ), "false", "first tab has aria-selected=false" ); + ok( panels.eq( 2 ).is( ":visible" ), "third panel is visible" ); + equal( panels.eq( 2 ).attr( "aria-expanded" ), "true", "third panel has aria-expanded=true" ); + equal( panels.eq( 2 ).attr( "aria-hidden" ), "false", "third panel has aria-hidden=false" ); + ok( panels.eq( 0 ).is( ":hidden" ), "first panel is hidden" ); + equal( panels.eq( 0 ).attr( "aria-expanded" ), "false", "first panel has aria-expanded=false" ); + equal( panels.eq( 0 ).attr( "aria-hidden" ), "true", "first panel has aria-hidden=true" ); + + setTimeout( start, 1 ); + } + + setTimeout( step1, 1 ); +}); + +asyncTest( "keyboard support - CTRL+UP, ALT+PAGE_DOWN, ALT+PAGE_UP", function() { + expect( 50 ); + var element = $( "#tabs1" ).tabs(), + tabs = element.find( ".ui-tabs-nav li" ), + panels = element.find( ".ui-tabs-panel" ), + keyCode = $.ui.keyCode; + + equal( tabs.filter( ".ui-state-focus" ).length, 0, "no tabs focused on init" ); + panels.attr( "tabindex", -1 ); + panels.eq( 0 ).simulate( "focus" ); + + function step1() { + strictEqual( document.activeElement, panels[ 0 ], "first panel is activeElement" ); + + panels.eq( 0 ).simulate( "keydown", { keyCode: keyCode.PAGE_DOWN, altKey: true } ); + strictEqual( document.activeElement, tabs[ 1 ], "second tab is activeElement" ); + ok( tabs.eq( 1 ).is( ".ui-state-focus" ), "ALT+PAGE_DOWN moves focus to next tab" ); + equal( tabs.eq( 1 ).attr( "aria-selected" ), "true", "second tab has aria-selected=true" ); + ok( panels.eq( 1 ).is( ":visible" ), "second panel is visible" ); + equal( panels.eq( 1 ).attr( "aria-expanded" ), "true", "second panel has aria-expanded=true" ); + equal( panels.eq( 1 ).attr( "aria-hidden" ), "false", "second panel has aria-hidden=false" ); + ok( panels.eq( 0 ).is( ":hidden" ), "first panel is hidden" ); + equal( panels.eq( 0 ).attr( "aria-expanded" ), "false", "first panel has aria-expanded=false" ); + equal( panels.eq( 0 ).attr( "aria-hidden" ), "true", "first panel has aria-hidden=true" ); + + tabs.eq( 1 ).simulate( "keydown", { keyCode: keyCode.PAGE_DOWN, altKey: true } ); + strictEqual( document.activeElement, tabs[ 2 ], "third tab is activeElement" ); + ok( tabs.eq( 2 ).is( ".ui-state-focus" ), "ALT+PAGE_DOWN moves focus to next tab" ); + equal( tabs.eq( 2 ).attr( "aria-selected" ), "true", "third tab has aria-selected=true" ); + ok( panels.eq( 2 ).is( ":visible" ), "third panel is visible" ); + equal( panels.eq( 2 ).attr( "aria-expanded" ), "true", "third panel has aria-expanded=true" ); + equal( panels.eq( 2 ).attr( "aria-hidden" ), "false", "third panel has aria-hidden=false" ); + ok( panels.eq( 1 ).is( ":hidden" ), "second panel is hidden" ); + equal( panels.eq( 1 ).attr( "aria-expanded" ), "false", "second panel has aria-expanded=false" ); + equal( panels.eq( 1 ).attr( "aria-hidden" ), "true", "second panel has aria-hidden=true" ); + + tabs.eq( 2 ).simulate( "keydown", { keyCode: keyCode.PAGE_DOWN, altKey: true } ); + strictEqual( document.activeElement, tabs[ 0 ], "first tab is activeElement" ); + ok( tabs.eq( 0 ).is( ".ui-state-focus" ), "ALT+PAGE_DOWN wraps focus to first tab" ); + equal( tabs.eq( 0 ).attr( "aria-selected" ), "true", "first tab has aria-selected=true" ); + ok( panels.eq( 0 ).is( ":visible" ), "first panel is visible" ); + equal( panels.eq( 0 ).attr( "aria-expanded" ), "true", "first panel has aria-expanded=true" ); + equal( panels.eq( 0 ).attr( "aria-hidden" ), "false", "first panel has aria-hidden=false" ); + ok( panels.eq( 2 ).is( ":hidden" ), "third panel is hidden" ); + equal( panels.eq( 2 ).attr( "aria-expanded" ), "false", "third panel has aria-expanded=false" ); + equal( panels.eq( 2 ).attr( "aria-hidden" ), "true", "third panel has aria-hidden=true" ); + + panels.eq( 0 ).simulate( "focus" ); + setTimeout( step2, 1 ); + } + + function step2() { + strictEqual( document.activeElement, panels[ 0 ], "first panel is activeElement" ); + + panels.eq( 0 ).simulate( "keydown", { keyCode: keyCode.PAGE_UP, altKey: true } ); + strictEqual( document.activeElement, tabs[ 2 ], "third tab is activeElement" ); + ok( tabs.eq( 2 ).is( ".ui-state-focus" ), "ALT+PAGE_UP wraps focus to last tab" ); + equal( tabs.eq( 2 ).attr( "aria-selected" ), "true", "third tab has aria-selected=true" ); + ok( panels.eq( 2 ).is( ":visible" ), "third panel is visible" ); + equal( panels.eq( 2 ).attr( "aria-expanded" ), "true", "third panel has aria-expanded=true" ); + equal( panels.eq( 2 ).attr( "aria-hidden" ), "false", "third panel has aria-hidden=false" ); + ok( panels.eq( 0 ).is( ":hidden" ), "first panel is hidden" ); + equal( panels.eq( 0 ).attr( "aria-expanded" ), "false", "first panel has aria-expanded=false" ); + equal( panels.eq( 0 ).attr( "aria-hidden" ), "true", "first panel has aria-hidden=true" ); + + tabs.eq( 2 ).simulate( "keydown", { keyCode: keyCode.PAGE_UP, altKey: true } ); + strictEqual( document.activeElement, tabs[ 1 ], "second tab is activeElement" ); + ok( tabs.eq( 1 ).is( ".ui-state-focus" ), "ALT+PAGE_UP moves focus to previous tab" ); + equal( tabs.eq( 1 ).attr( "aria-selected" ), "true", "second tab has aria-selected=true" ); + ok( panels.eq( 1 ).is( ":visible" ), "second panel is visible" ); + equal( panels.eq( 1 ).attr( "aria-expanded" ), "true", "second panel has aria-expanded=true" ); + equal( panels.eq( 1 ).attr( "aria-hidden" ), "false", "second panel has aria-hidden=false" ); + ok( panels.eq( 2 ).is( ":hidden" ), "third panel is hidden" ); + equal( panels.eq( 2 ).attr( "aria-expanded" ), "false", "third panel has aria-expanded=false" ); + equal( panels.eq( 2 ).attr( "aria-hidden" ), "true", "third panel has aria-hidden=true" ); + + panels.eq( 1 ).simulate( "focus" ); + setTimeout( step3, 1 ); + } + + function step3() { + strictEqual( document.activeElement, panels[ 1 ], "second panel is activeElement" ); + + panels.eq( 1 ).simulate( "keydown", { keyCode: keyCode.UP, ctrlKey: true } ); + strictEqual( document.activeElement, tabs[ 1 ], "second tab is activeElement" ); + + setTimeout( start, 1 ); + } + + setTimeout( step1, 1 ); +}); + +test( "#3627 - Ajax tab with url containing a fragment identifier fails to load", function() { + expect( 1 ); + + $( "#tabs2" ).tabs({ + active: 2, + beforeLoad: function( event, ui ) { + event.preventDefault(); + ok( /test.html$/.test( ui.ajaxSettings.url ), "should ignore fragment identifier" ); + } + }); +}); + +test( "#4033 - IE expands hash to full url and misinterprets tab as ajax", function() { + expect( 2 ); + + var element = $("<div><ul><li><a href='#tab'>Tab</a></li></ul><div id='tab'></div></div>"); + element.appendTo("#qunit-fixture"); + element.tabs({ + beforeLoad: function() { + event.preventDefault(); + ok( false, "should not be an ajax tab" ); + } + }); + + equal( element.find(".ui-tabs-nav li").attr("aria-controls"), "tab", "aria-contorls attribute is correct" ); + state( element, 1 ); +}); + +}( jQuery ) ); diff --git a/apps/it/static/js/ui/tests/unit/tabs/tabs_events.js b/apps/it/static/js/ui/tests/unit/tabs/tabs_events.js new file mode 100644 index 0000000..f9b1755 --- /dev/null +++ b/apps/it/static/js/ui/tests/unit/tabs/tabs_events.js @@ -0,0 +1,318 @@ +(function( $ ) { + +var state = TestHelpers.tabs.state; + +module( "tabs: events" ); + +test( "create", function() { + expect( 10 ); + + var element = $( "#tabs1" ), + tabs = element.find( "ul li" ), + panels = element.children( "div" ); + + element.tabs({ + create: function( event, ui ) { + equal( ui.tab.length, 1, "tab length" ); + strictEqual( ui.tab[ 0 ], tabs[ 0 ], "tab" ); + equal( ui.panel.length, 1, "panel length" ); + strictEqual( ui.panel[ 0 ], panels[ 0 ], "panel" ); + } + }); + element.tabs( "destroy" ); + + element.tabs({ + active: 2, + create: function( event, ui ) { + equal( ui.tab.length, 1, "tab length" ); + strictEqual( ui.tab[ 0 ], tabs[ 2 ], "tab" ); + equal( ui.panel.length, 1, "panel length" ); + strictEqual( ui.panel[ 0 ], panels[ 2 ], "panel" ); + } + }); + element.tabs( "destroy" ); + + element.tabs({ + active: false, + collapsible: true, + create: function( event, ui ) { + equal( ui.tab.length, 0, "tab length" ); + equal( ui.panel.length, 0, "panel length" ); + } + }); + element.tabs( "destroy" ); +}); + +test( "beforeActivate", function() { + expect( 38 ); + + var element = $( "#tabs1" ).tabs({ + active: false, + collapsible: true + }), + tabs = element.find( ".ui-tabs-nav li" ), + anchors = tabs.find( ".ui-tabs-anchor" ), + panels = element.find( ".ui-tabs-panel" ); + + // from collapsed + element.one( "tabsbeforeactivate", function( event, ui ) { + ok( !( "originalEvent" in event ), "originalEvent" ); + equal( ui.oldTab.length, 0, "oldTab length" ); + equal( ui.oldPanel.length, 0, "oldPanel length" ); + equal( ui.newTab.length, 1, "newTab length" ); + strictEqual( ui.newTab[ 0 ], tabs[ 0 ], "newTab" ); + equal( ui.newPanel.length, 1, "newPanel length" ); + strictEqual( ui.newPanel[ 0 ], panels[ 0 ], "newPanel" ); + state( element, 0, 0, 0 ); + }); + element.tabs( "option", "active", 0 ); + state( element, 1, 0, 0 ); + + // switching tabs + element.one( "tabsbeforeactivate", function( event, ui ) { + equal( event.originalEvent.type, "click", "originalEvent" ); + equal( ui.oldTab.length, 1, "oldTab length" ); + strictEqual( ui.oldTab[ 0 ], tabs[ 0 ], "oldTab" ); + equal( ui.oldPanel.length, 1, "oldPanel length" ); + strictEqual( ui.oldPanel[ 0 ], panels[ 0 ], "oldPanel" ); + equal( ui.newTab.length, 1, "newTab length" ); + strictEqual( ui.newTab[ 0 ], tabs[ 1 ], "newTab" ); + equal( ui.newPanel.length, 1, "newPanel length" ); + strictEqual( ui.newPanel[ 0 ], panels[ 1 ], "newPanel" ); + state( element, 1, 0, 0 ); + }); + anchors.eq( 1 ).click(); + state( element, 0, 1, 0 ); + + // collapsing + element.one( "tabsbeforeactivate", function( event, ui ) { + ok( !( "originalEvent" in event ), "originalEvent" ); + equal( ui.oldTab.length, 1, "oldTab length" ); + strictEqual( ui.oldTab[ 0 ], tabs[ 1 ], "oldTab" ); + equal( ui.oldPanel.length, 1, "oldPanel length" ); + strictEqual( ui.oldPanel[ 0 ], panels[ 1 ], "oldPanel" ); + equal( ui.newTab.length, 0, "newTab length" ); + equal( ui.newPanel.length, 0, "newPanel length" ); + state( element, 0, 1, 0 ); + }); + element.tabs( "option", "active", false ); + state( element, 0, 0, 0 ); + + // prevent activation + element.one( "tabsbeforeactivate", function( event, ui ) { + ok( !( "originalEvent" in event ), "originalEvent" ); + equal( ui.oldTab.length, 0, "oldTab length" ); + equal( ui.oldPanel.length, 0, "oldTab" ); + equal( ui.newTab.length, 1, "newTab length" ); + strictEqual( ui.newTab[ 0 ], tabs[ 1 ], "newTab" ); + equal( ui.newPanel.length, 1, "newPanel length" ); + strictEqual( ui.newPanel[ 0 ], panels[ 1 ], "newPanel" ); + event.preventDefault(); + state( element, 0, 0, 0 ); + }); + element.tabs( "option", "active", 1 ); + state( element, 0, 0, 0 ); +}); + +test( "activate", function() { + expect( 30 ); + + var element = $( "#tabs1" ).tabs({ + active: false, + collapsible: true + }), + tabs = element.find( ".ui-tabs-nav li" ), + anchors = element.find( ".ui-tabs-anchor" ), + panels = element.find( ".ui-tabs-panel" ); + + // from collapsed + element.one( "tabsactivate", function( event, ui ) { + ok( !( "originalEvent" in event ), "originalEvent" ); + equal( ui.oldTab.length, 0, "oldTab length" ); + equal( ui.oldPanel.length, 0, "oldPanel length" ); + equal( ui.newTab.length, 1, "newTab length" ); + strictEqual( ui.newTab[ 0 ], tabs[ 0 ], "newTab" ); + equal( ui.newPanel.length, 1, "newPanel length" ); + strictEqual( ui.newPanel[ 0 ], panels[ 0 ], "newPanel" ); + state( element, 1, 0, 0 ); + }); + element.tabs( "option", "active", 0 ); + state( element, 1, 0, 0 ); + + // switching tabs + element.one( "tabsactivate", function( event, ui ) { + equal( event.originalEvent.type, "click", "originalEvent" ); + equal( ui.oldTab.length, 1, "oldTab length" ); + strictEqual( ui.oldTab[ 0 ], tabs[ 0 ], "oldTab" ); + equal( ui.oldPanel.length, 1, "oldPanel length" ); + strictEqual( ui.oldPanel[ 0 ], panels[ 0 ], "oldPanel" ); + equal( ui.newTab.length, 1, "newTab length" ); + strictEqual( ui.newTab[ 0 ], tabs[ 1 ], "newTab" ); + equal( ui.newPanel.length, 1, "newPanel length" ); + strictEqual( ui.newPanel[ 0 ], panels[ 1 ], "newPanel" ); + state( element, 0, 1, 0 ); + }); + anchors.eq( 1 ).click(); + state( element, 0, 1, 0 ); + + // collapsing + element.one( "tabsactivate", function( event, ui ) { + ok( !( "originalEvent" in event ), "originalEvent" ); + equal( ui.oldTab.length, 1, "oldTab length" ); + strictEqual( ui.oldTab[ 0 ], tabs[ 1 ], "oldTab" ); + equal( ui.oldPanel.length, 1, "oldPanel length" ); + strictEqual( ui.oldPanel[ 0 ], panels[ 1 ], "oldPanel" ); + equal( ui.newTab.length, 0, "newTab length" ); + equal( ui.newPanel.length, 0, "newPanel length" ); + state( element, 0, 0, 0 ); + }); + element.tabs( "option", "active", false ); + state( element, 0, 0, 0 ); + + // prevent activation + element.one( "tabsbeforeactivate", function( event ) { + ok( true, "tabsbeforeactivate" ); + event.preventDefault(); + }); + element.one( "tabsactivate", function() { + ok( false, "tabsactivate" ); + }); + element.tabs( "option", "active", 1 ); +}); + +test( "beforeLoad", function() { + expect( 32 ); + + var tab, panelId, panel, + element = $( "#tabs2" ); + + // init + element.one( "tabsbeforeload", function( event, ui ) { + tab = element.find( ".ui-tabs-nav li" ).eq( 2 ); + panelId = tab.attr( "aria-controls" ); + panel = $( "#" + panelId ); + + ok( !( "originalEvent" in event ), "originalEvent" ); + ok( "abort" in ui.jqXHR, "jqXHR" ); + ok( ui.ajaxSettings.url, "data/test.html", "ajaxSettings.url" ); + equal( ui.tab.length, 1, "tab length" ); + strictEqual( ui.tab[ 0 ], tab[ 0 ], "tab" ); + equal( ui.panel.length, 1, "panel length" ); + strictEqual( ui.panel[ 0 ], panel[ 0 ], "panel" ); + equal( ui.panel.html(), "", "panel html" ); + event.preventDefault(); + state( element, 0, 0, 1, 0, 0 ); + }); + element.tabs({ active: 2 }); + state( element, 0, 0, 1, 0, 0 ); + equal( panel.html(), "", "panel html after" ); + element.tabs( "destroy" ); + + // .option() + element.one( "tabsbeforeload", function( event, ui ) { + tab = element.find( ".ui-tabs-nav li" ).eq( 2 ); + panelId = tab.attr( "aria-controls" ); + panel = $( "#" + panelId ); + + ok( !( "originalEvent" in event ), "originalEvent" ); + ok( "abort" in ui.jqXHR, "jqXHR" ); + ok( ui.ajaxSettings.url, "data/test.html", "ajaxSettings.url" ); + equal( ui.tab.length, 1, "tab length" ); + strictEqual( ui.tab[ 0 ], tab[ 0 ], "tab" ); + equal( ui.panel.length, 1, "panel length" ); + strictEqual( ui.panel[ 0 ], panel[ 0 ], "panel" ); + equal( ui.panel.html(), "", "panel html" ); + event.preventDefault(); + state( element, 1, 0, 0, 0, 0 ); + }); + element.tabs(); + element.tabs( "option", "active", 2 ); + state( element, 0, 0, 1, 0, 0 ); + equal( panel.html(), "", "panel html after" ); + + // click, change panel content + element.one( "tabsbeforeload", function( event, ui ) { + tab = element.find( ".ui-tabs-nav li" ).eq( 3 ); + panelId = tab.attr( "aria-controls" ); + panel = $( "#" + panelId ); + + equal( event.originalEvent.type, "click", "originalEvent" ); + ok( "abort" in ui.jqXHR, "jqXHR" ); + ok( ui.ajaxSettings.url, "data/test.html", "ajaxSettings.url" ); + equal( ui.tab.length, 1, "tab length" ); + strictEqual( ui.tab[ 0 ], tab[ 0 ], "tab" ); + equal( ui.panel.length, 1, "panel length" ); + strictEqual( ui.panel[ 0 ], panel[ 0 ], "panel" ); + ui.panel.html( "<p>testing</p>" ); + event.preventDefault(); + state( element, 0, 0, 1, 0, 0 ); + }); + element.find( ".ui-tabs-nav .ui-tabs-anchor" ).eq( 3 ).click(); + state( element, 0, 0, 0, 1, 0 ); + // .toLowerCase() is needed to convert <P> to <p> in old IEs + equal( panel.html().toLowerCase(), "<p>testing</p>", "panel html after" ); +}); + +asyncTest( "load", function() { + expect( 21 ); + + var tab, panelId, panel, + element = $( "#tabs2" ); + + // init + element.one( "tabsload", function( event, ui ) { + tab = element.find( ".ui-tabs-nav li" ).eq( 2 ); + panelId = tab.attr( "aria-controls" ); + panel = $( "#" + panelId ); + + ok( !( "originalEvent" in event ), "originalEvent" ); + equal( ui.tab.length, 1, "tab length" ); + strictEqual( ui.tab[ 0 ], tab[ 0 ], "tab" ); + equal( ui.panel.length, 1, "panel length" ); + strictEqual( ui.panel[ 0 ], panel[ 0 ], "panel" ); + equal( ui.panel.find( "p" ).length, 1, "panel html" ); + state( element, 0, 0, 1, 0, 0 ); + tabsload1(); + }); + element.tabs({ active: 2 }); + + function tabsload1() { + // .option() + element.one( "tabsload", function( event, ui ) { + tab = element.find( ".ui-tabs-nav li" ).eq( 3 ); + panelId = tab.attr( "aria-controls" ); + panel = $( "#" + panelId ); + + ok( !( "originalEvent" in event ), "originalEvent" ); + equal( ui.tab.length, 1, "tab length" ); + strictEqual( ui.tab[ 0 ], tab[ 0 ], "tab" ); + equal( ui.panel.length, 1, "panel length" ); + strictEqual( ui.panel[ 0 ], panel[ 0 ], "panel" ); + equal( ui.panel.find( "p" ).length, 1, "panel html" ); + state( element, 0, 0, 0, 1, 0 ); + tabsload2(); + }); + element.tabs( "option", "active", 3 ); + } + + function tabsload2() { + // click, change panel content + element.one( "tabsload", function( event, ui ) { + tab = element.find( ".ui-tabs-nav li" ).eq( 4 ); + panelId = tab.attr( "aria-controls" ); + panel = $( "#" + panelId ); + + equal( event.originalEvent.type, "click", "originalEvent" ); + equal( ui.tab.length, 1, "tab length" ); + strictEqual( ui.tab[ 0 ], tab[ 0 ], "tab" ); + equal( ui.panel.length, 1, "panel length" ); + strictEqual( ui.panel[ 0 ], panel[ 0 ], "panel" ); + equal( ui.panel.find( "p" ).length, 1, "panel html" ); + state( element, 0, 0, 0, 0, 1 ); + start(); + }); + element.find( ".ui-tabs-nav .ui-tabs-anchor" ).eq( 4 ).click(); + } +}); + +}( jQuery ) ); diff --git a/apps/it/static/js/ui/tests/unit/tabs/tabs_methods.js b/apps/it/static/js/ui/tests/unit/tabs/tabs_methods.js new file mode 100644 index 0000000..a3d663f --- /dev/null +++ b/apps/it/static/js/ui/tests/unit/tabs/tabs_methods.js @@ -0,0 +1,270 @@ +(function( $ ) { + +var disabled = TestHelpers.tabs.disabled, + state = TestHelpers.tabs.state; + +module( "tabs: methods" ); + +test( "destroy", function() { + expect( 2 ); + domEqual( "#tabs1", function() { + $( "#tabs1" ).tabs().tabs( "destroy" ); + }); + domEqual( "#tabs2", function() { + $( "#tabs2" ).tabs().tabs( "destroy" ); + }); +}); + +test( "enable", function() { + expect( 8 ); + + var element = $( "#tabs1" ).tabs({ disabled: true }); + disabled( element, true ); + element.tabs( "enable" ); + disabled( element, false ); + element.tabs( "destroy" ); + + element.tabs({ disabled: [ 0, 1 ] }); + disabled( element, [ 0, 1 ] ); + element.tabs( "enable" ); + disabled( element, false ); +}); + +test( "enable( index )", function() { + expect( 10 ); + + var element = $( "#tabs1" ).tabs({ disabled: true }); + disabled( element, true ); + // fully disabled -> partially disabled + element.tabs( "enable", 1 ); + disabled( element, [ 0, 2 ] ); + // partially disabled -> partially disabled + element.tabs( "enable", 2 ); + disabled( element, [ 0 ] ); + // already enabled tab, no change + element.tabs( "enable", 2 ); + disabled( element, [ 0 ] ); + // partially disabled -> fully enabled + element.tabs( "enable", 0 ); + disabled( element, false ); +}); + +test( "disable", function() { + expect( 8 ); + + var element = $( "#tabs1" ).tabs({ disabled: false }); + disabled( element, false ); + element.tabs( "disable" ); + disabled( element, true ); + element.tabs( "destroy" ); + + element.tabs({ disabled: [ 0, 1 ] }); + disabled( element, [ 0, 1 ] ); + element.tabs( "disable" ); + disabled( element, true ); +}); + +test( "disable( index )", function() { + expect( 10 ); + + var element = $( "#tabs1" ).tabs({ disabled: false }); + disabled( element, false ); + // fully enabled -> partially disabled + element.tabs( "disable", 1 ); + disabled( element, [ 1 ] ); + // partially disabled -> partially disabled + element.tabs( "disable", 2 ); + disabled( element, [ 1, 2 ] ); + // already disabled tab, no change + element.tabs( "disable", 2 ); + disabled( element, [ 1, 2 ] ); + // partially disabled -> fully disabled + element.tabs( "disable", 0 ); + disabled( element, true ); +}); + +test( "refresh", function() { + expect( 27 ); + + var element = $( "#tabs1" ).tabs(); + state( element, 1, 0, 0 ); + disabled( element, false ); + + // disable tab via markup + element.find( ".ui-tabs-nav li" ).eq( 1 ).addClass( "ui-state-disabled" ); + element.tabs( "refresh" ); + state( element, 1, 0, 0 ); + disabled( element, [ 1 ] ); + + // add remote tab + element.find( ".ui-tabs-nav" ).append( "<li id='newTab'><a href='data/test.html'>new</a></li>" ); + element.tabs( "refresh" ); + state( element, 1, 0, 0, 0 ); + disabled( element, [ 1 ] ); + equal( element.find( "#" + $( "#newTab" ).attr( "aria-controls" ) ).length, 1, + "panel added for remote tab" ); + + // remove all tabs + element.find( ".ui-tabs-nav li, .ui-tabs-panel" ).remove(); + element.tabs( "refresh" ); + state( element ); + equal( element.tabs( "option", "active" ), false, "no active tab" ); + + // add tabs + element.find( ".ui-tabs-nav" ) + .append( "<li class='ui-state-disabled'><a href='#newTab2'>new 2</a></li>" ) + .append( "<li><a href='#newTab3'>new 3</a></li>" ) + .append( "<li><a href='#newTab4'>new 4</a></li>" ) + .append( "<li><a href='#newTab5'>new 5</a></li>" ); + element + .append( "<div id='newTab2'>new 2</div>" ) + .append( "<div id='newTab3'>new 3</div>" ) + .append( "<div id='newTab4'>new 4</div>" ) + .append( "<div id='newTab5'>new 5</div>" ); + element.tabs( "refresh" ); + state( element, 0, 0, 0, 0 ); + disabled( element, [ 0 ] ); + + // activate third tab + element.tabs( "option", "active", 2 ); + state( element, 0, 0, 1, 0 ); + disabled( element, [ 0 ] ); + + // remove fourth tab, third tab should stay active + element.find( ".ui-tabs-nav li" ).eq( 3 ).remove(); + element.find( ".ui-tabs-panel" ).eq( 3 ).remove(); + element.tabs( "refresh" ); + state( element, 0, 0, 1 ); + disabled( element, [ 0 ] ); + + // remove third (active) tab, second tab should become active + element.find( ".ui-tabs-nav li" ).eq( 2 ).remove(); + element.find( ".ui-tabs-panel" ).eq( 2 ).remove(); + element.tabs( "refresh" ); + state( element, 0, 1 ); + disabled( element, [ 0 ] ); + + // remove first tab, previously active tab (now first) should stay active + element.find( ".ui-tabs-nav li" ).eq( 0 ).remove(); + element.find( ".ui-tabs-panel" ).eq( 0 ).remove(); + element.tabs( "refresh" ); + state( element, 1 ); + disabled( element, false ); +}); + +test( "refresh - looping", function() { + expect( 6 ); + + var element = $( "#tabs1" ).tabs({ + disabled: [ 0 ], + active: 1 + }); + state( element, 0, 1, 0 ); + disabled( element, [ 0 ] ); + + // remove active, jump to previous + // previous is disabled, just back one more + // reached first tab, move to end + // activate last tab + element.find( ".ui-tabs-nav li" ).eq( 2 ).remove(); + element.tabs( "refresh" ); + state( element, 0, 1 ); + disabled( element, [ 0 ] ); +}); + +asyncTest( "load", function() { + expect( 30 ); + + var element = $( "#tabs2" ).tabs(); + + // load content of inactive tab + // useful for preloading content with custom caching + element.one( "tabsbeforeload", function( event, ui ) { + var tab = element.find( ".ui-tabs-nav li" ).eq( 3 ), + panelId = tab.attr( "aria-controls" ), + panel = $( "#" + panelId ); + + ok( !( "originalEvent" in event ), "originalEvent" ); + equal( ui.tab.length, 1, "tab length" ); + strictEqual( ui.tab[ 0 ], tab[ 0 ], "tab" ); + equal( ui.panel.length, 1, "panel length" ); + strictEqual( ui.panel[ 0 ], panel[ 0 ], "panel" ); + state( element, 1, 0, 0, 0, 0 ); + }); + element.one( "tabsload", function( event, ui ) { + // TODO: remove wrapping in 2.0 + var uiTab = $( ui.tab ), + uiPanel = $( ui.panel ), + tab = element.find( ".ui-tabs-nav li" ).eq( 3 ), + panelId = tab.attr( "aria-controls" ), + panel = $( "#" + panelId ); + + ok( !( "originalEvent" in event ), "originalEvent" ); + equal( uiTab.length, 1, "tab length" ); + strictEqual( uiTab[ 0 ], tab[ 0 ], "tab" ); + equal( uiPanel.length, 1, "panel length" ); + strictEqual( uiPanel[ 0 ], panel[ 0 ], "panel" ); + equal( uiPanel.find( "p" ).length, 1, "panel html" ); + state( element, 1, 0, 0, 0, 0 ); + setTimeout( tabsload1, 100 ); + }); + element.tabs( "load", 3 ); + state( element, 1, 0, 0, 0, 0 ); + + function tabsload1() { + // no need to test details of event (tested in events tests) + element.one( "tabsbeforeload", function() { + ok( true, "tabsbeforeload invoked" ); + }); + element.one( "tabsload", function() { + ok( true, "tabsload invoked" ); + setTimeout( tabsload2, 100 ); + }); + element.tabs( "option", "active", 3 ); + state( element, 0, 0, 0, 1, 0 ); + } + + function tabsload2() { + // reload content of active tab + element.one( "tabsbeforeload", function( event, ui ) { + var tab = element.find( ".ui-tabs-nav li" ).eq( 3 ), + panelId = tab.attr( "aria-controls" ), + panel = $( "#" + panelId ); + + ok( !( "originalEvent" in event ), "originalEvent" ); + equal( ui.tab.length, 1, "tab length" ); + strictEqual( ui.tab[ 0 ], tab[ 0 ], "tab" ); + equal( ui.panel.length, 1, "panel length" ); + strictEqual( ui.panel[ 0 ], panel[ 0 ], "panel" ); + state( element, 0, 0, 0, 1, 0 ); + }); + element.one( "tabsload", function( event, ui ) { + // TODO: remove wrapping in 2.0 + var uiTab = $( ui.tab ), + uiPanel = $( ui.panel ), + tab = element.find( ".ui-tabs-nav li" ).eq( 3 ), + panelId = tab.attr( "aria-controls" ), + panel = $( "#" + panelId ); + + ok( !( "originalEvent" in event ), "originalEvent" ); + equal( uiTab.length, 1, "tab length" ); + strictEqual( uiTab[ 0 ], tab[ 0 ], "tab" ); + equal( uiPanel.length, 1, "panel length" ); + strictEqual( uiPanel[ 0 ], panel[ 0 ], "panel" ); + state( element, 0, 0, 0, 1, 0 ); + start(); + }); + element.tabs( "load", 3 ); + state( element, 0, 0, 0, 1, 0 ); + } +}); + +test( "widget", function() { + expect( 2 ); + var element = $( "#tabs1" ).tabs(), + widgetElement = element.tabs( "widget" ); + equal( widgetElement.length, 1, "one element" ); + strictEqual( widgetElement[ 0 ], element[ 0 ], "same element" ); +}); + +}( jQuery ) ); diff --git a/apps/it/static/js/ui/tests/unit/tabs/tabs_options.js b/apps/it/static/js/ui/tests/unit/tabs/tabs_options.js new file mode 100644 index 0000000..c78c42b --- /dev/null +++ b/apps/it/static/js/ui/tests/unit/tabs/tabs_options.js @@ -0,0 +1,345 @@ +(function( $ ) { + +var disabled = TestHelpers.tabs.disabled, + equalHeight = TestHelpers.tabs.equalHeight, + state = TestHelpers.tabs.state; + +module( "tabs: options" ); + +test( "{ active: default }", function() { + expect( 6 ); + + var element = $( "#tabs1" ).tabs(); + equal( element.tabs( "option", "active" ), 0, "should be 0 by default" ); + state( element, 1, 0, 0 ); + element.tabs( "destroy" ); + + location.hash = "#fragment-3"; + element = $( "#tabs1" ).tabs(); + equal( element.tabs( "option", "active" ), 2, "should be 2 based on URL" ); + state( element, 0, 0, 1 ); + element.tabs( "destroy" ); + + location.hash = "#custom-id"; + element = $( "#tabs2" ).tabs(); + equal( element.tabs( "option", "active" ), 3, "should be 3 based on URL" ); + state( element, 0, 0, 0, 1, 0 ); + element.tabs( "destroy" ); + location.hash = "#"; +}); + +test( "{ active: false }", function() { + expect( 7 ); + + var element = $( "#tabs1" ).tabs({ + active: false, + collapsible: true + }); + state( element, 0, 0, 0 ); + equal( element.find( ".ui-tabs-nav .ui-state-active" ).length, 0, "no tabs selected" ); + strictEqual( element.tabs( "option", "active" ), false ); + + element.tabs( "option", "collapsible", false ); + state( element, 1, 0, 0 ); + equal( element.tabs( "option", "active" ), 0 ); + + element.tabs( "destroy" ); + element.tabs({ + active: false + }); + state( element, 1, 0, 0 ); + strictEqual( element.tabs( "option", "active" ), 0 ); +}); + +test( "{ active: Number }", function() { + expect( 8 ); + + var element = $( "#tabs1" ).tabs({ + active: 2 + }); + equal( element.tabs( "option", "active" ), 2 ); + state( element, 0, 0, 1 ); + + element.tabs( "option", "active", 0 ); + equal( element.tabs( "option", "active" ), 0 ); + state( element, 1, 0, 0 ); + + element.find( ".ui-tabs-nav .ui-tabs-anchor" ).eq( 1 ).click(); + equal( element.tabs( "option", "active" ), 1 ); + state( element, 0, 1, 0 ); + + element.tabs( "option", "active", 10 ); + equal( element.tabs( "option", "active" ), 1 ); + state( element, 0, 1, 0 ); +}); + +test( "{ active: -Number }", function() { + expect( 8 ); + + var element = $( "#tabs1" ).tabs({ + active: -1 + }); + equal( element.tabs( "option", "active" ), 2 ); + state( element, 0, 0, 1 ); + + element.tabs( "option", "active", -2 ); + equal( element.tabs( "option", "active" ), 1 ); + state( element, 0, 1, 0 ); + + element.tabs( "option", "active", -10 ); + equal( element.tabs( "option", "active" ), 1 ); + state( element, 0, 1, 0 ); + + element.tabs( "option", "active", -3 ); + equal( element.tabs( "option", "active" ), 0 ); + state( element, 1, 0, 0 ); +}); + +test( "active - mismatched tab/panel order", function() { + expect( 3 ); + + location.hash = "#tabs7-2"; + var element = $( "#tabs7" ).tabs(); + equal( element.tabs( "option", "active" ), 1, "should be 1 based on URL" ); + state( element, 0, 1 ); + element.tabs( "option", "active", 0 ); + state( element, 1, 0 ); + location.hash = "#"; +}); + +test( "{ collapsible: false }", function() { + expect( 4 ); + + var element = $( "#tabs1" ).tabs({ + active: 1 + }); + element.tabs( "option", "active", false ); + equal( element.tabs( "option", "active" ), 1 ); + state( element, 0, 1, 0 ); + + element.find( ".ui-state-active .ui-tabs-anchor" ).eq( 1 ).click(); + equal( element.tabs( "option", "active" ), 1 ); + state( element, 0, 1, 0 ); +}); + +test( "{ collapsible: true }", function() { + expect( 6 ); + + var element = $( "#tabs1" ).tabs({ + active: 1, + collapsible: true + }); + + element.tabs( "option", "active", false ); + equal( element.tabs( "option", "active" ), false ); + state( element, 0, 0, 0 ); + + element.tabs( "option", "active", 1 ); + equal( element.tabs( "option", "active" ), 1 ); + state( element, 0, 1, 0 ); + + element.find( ".ui-state-active .ui-tabs-anchor" ).click(); + equal( element.tabs( "option", "active" ), false ); + state( element, 0, 0, 0 ); +}); + +test( "disabled", function() { + expect( 10 ); + + // fully enabled by default + var element = $( "#tabs1" ).tabs(); + disabled( element, false ); + + // disable single tab + element.tabs( "option", "disabled", [ 1 ] ); + disabled( element, [ 1 ] ); + + // disabled active tab + element.tabs( "option", "disabled", [ 0, 1 ] ); + disabled( element, [ 0, 1 ] ); + + // disable all tabs + element.tabs( "option", "disabled", [ 0, 1, 2 ] ); + disabled( element, true ); + + // enable all tabs + element.tabs( "option", "disabled", [] ); + disabled( element, false ); +}); + +test( "{ event: null }", function() { + expect( 5 ); + + var element = $( "#tabs1" ).tabs({ + event: null + }); + state( element, 1, 0, 0 ); + + element.tabs( "option", "active", 1 ); + equal( element.tabs( "option", "active" ), 1 ); + state( element, 0, 1, 0 ); + + // ensure default click handler isn't bound + element.find( ".ui-tabs-nav .ui-tabs-anchor" ).eq( 2 ).click(); + equal( element.tabs( "option", "active" ), 1 ); + state( element, 0, 1, 0 ); +}); + +test( "{ event: custom }", function() { + expect( 11 ); + + var element = $( "#tabs1" ).tabs({ + event: "custom1 custom2" + }); + state( element, 1, 0, 0 ); + + element.find( ".ui-tabs-nav .ui-tabs-anchor" ).eq( 1 ).trigger( "custom1" ); + equal( element.tabs( "option", "active" ), 1 ); + state( element, 0, 1, 0 ); + + // ensure default click handler isn't bound + element.find( ".ui-tabs-nav .ui-tabs-anchor" ).eq( 2 ).trigger( "click" ); + equal( element.tabs( "option", "active" ), 1 ); + state( element, 0, 1, 0 ); + + element.find( ".ui-tabs-nav .ui-tabs-anchor" ).eq( 2 ).trigger( "custom2" ); + equal( element.tabs( "option", "active" ), 2 ); + state( element, 0, 0, 1 ); + + element.tabs( "option", "event", "custom3" ); + + // ensure old event handlers are unbound + element.find( ".ui-tabs-nav .ui-tabs-anchor" ).eq( 1 ).trigger( "custom1" ); + equal( element.tabs( "option", "active" ), 2 ); + state( element, 0, 0, 1 ); + + element.find( ".ui-tabs-nav .ui-tabs-anchor" ).eq( 1 ).trigger( "custom3" ); + equal( element.tabs( "option", "active" ), 1 ); + state( element, 0, 1, 0 ); +}); + +test( "{ heightStyle: 'auto' }", function() { + expect( 2 ); + var element = $( "#tabs8" ).tabs({ heightStyle: "auto" }); + equalHeight( element, 45 ); +}); + +test( "{ heightStyle: 'content' }", function() { + expect( 2 ); + var element = $( "#tabs8" ).tabs({ heightStyle: "content" }), + sizes = element.find( ".ui-tabs-panel" ).map(function() { + return $( this ).height(); + }).get(); + equal( sizes[ 0 ], 45 ); + equal( sizes[ 1 ], 15 ); +}); + +test( "{ heightStyle: 'fill' }", function() { + expect( 4 ); + $( "#tabs8Wrapper" ).height( 500 ); + var element = $( "#tabs8" ).tabs({ heightStyle: "fill" }); + equalHeight( element, 485 ); + element.tabs( "destroy" ); + + element = $( "#tabs8" ).css({ + "border": "1px solid black", + "padding": "1px 0" + }); + element.tabs({ heightStyle: "fill" }); + equalHeight( element, 481 ); +}); + +test( "{ heightStyle: 'fill' } with sibling", function() { + expect( 2 ); + $( "#tabs8Wrapper" ).height( 500 ); + $( "<p>Lorem Ipsum</p>" ) + .css({ + height: 50, + marginTop: 20, + marginBottom: 30 + }) + .prependTo( "#tabs8Wrapper" ); + var element = $( "#tabs8" ).tabs({ heightStyle: "fill" }); + equalHeight( element, 385 ); +}); + +test( "{ heightStyle: 'fill' } with multiple siblings", function() { + expect( 2 ); + $( "#tabs8Wrapper" ).height( 500 ); + $( "<p>Lorem Ipsum</p>" ) + .css({ + height: 50, + marginTop: 20, + marginBottom: 30 + }) + .prependTo( "#tabs8Wrapper" ); + $( "<p>Lorem Ipsum</p>" ) + .css({ + height: 50, + marginTop: 20, + marginBottom: 30, + position: "absolute" + }) + .prependTo( "#tabs8Wrapper" ); + $( "<p>Lorem Ipsum</p>" ) + .css({ + height: 25, + marginTop: 10, + marginBottom: 15 + }) + .prependTo( "#tabs8Wrapper" ); + var element = $( "#tabs8" ).tabs({ heightStyle: "fill" }); + equalHeight( element, 335 ); +}); + +test( "hide and show: false", function() { + expect( 3 ); + var element = $( "#tabs1" ).tabs({ + show: false, + hide: false + }), + widget = element.data( "ui-tabs" ), + panels = element.find( ".ui-tabs-panel" ); + widget._show = function() { + ok( false, "_show() called" ); + }; + widget._hide = function() { + ok( false, "_hide() called" ); + }; + + ok( panels.eq( 0 ).is( ":visible" ), "first panel visible" ); + element.tabs( "option", "active", 1 ); + ok( panels.eq( 0 ).is( ":hidden" ), "first panel hidden" ); + ok( panels.eq( 1 ).is( ":visible" ), "second panel visible" ); +}); + +asyncTest( "hide and show - animation", function() { + expect( 5 ); + var element = $( "#tabs1" ).tabs({ + show: "drop", + hide: 2000 + }), + widget = element.data( "ui-tabs" ), + panels = element.find( ".ui-tabs-panel" ); + widget._show = function( element, options, callback ) { + strictEqual( element[ 0 ], panels[ 1 ], "correct element in _show()" ); + equal( options, "drop", "correct options in _show()" ); + setTimeout(function() { + callback(); + }, 1 ); + }; + widget._hide = function( element, options, callback ) { + strictEqual( element[ 0 ], panels[ 0 ], "correct element in _hide()" ); + equal( options, 2000, "correct options in _hide()" ); + setTimeout(function() { + callback(); + start(); + }, 1 ); + }; + + ok( panels.eq( 0 ).is( ":visible" ), "first panel visible" ); + element.tabs( "option", "active", 1 ); +}); + + +}( jQuery ) ); diff --git a/apps/it/static/js/ui/tests/unit/tabs/tabs_test_helpers.js b/apps/it/static/js/ui/tests/unit/tabs/tabs_test_helpers.js new file mode 100644 index 0000000..0c93091 --- /dev/null +++ b/apps/it/static/js/ui/tests/unit/tabs/tabs_test_helpers.js @@ -0,0 +1,67 @@ +TestHelpers.tabs = { + disabled: function( tabs, state ) { + var expected, actual, + internalState = tabs.tabs( "option", "disabled" ); + + if ( internalState === false ) { + internalState = []; + } + if ( internalState === true ) { + internalState = $.map( new Array( tabs.find( ".ui-tabs-nav li" ).length ), function( _, index ) { + return index; + }); + } + + expected = $.map( new Array( tabs.find ( ".ui-tabs-nav li" ).length ), function( _, index ) { + if ( typeof state === "boolean" ) { + return state ? 1 : 0; + } else { + return $.inArray( index, state ) !== -1 ? 1 : 0; + } + }); + + actual = tabs.find( ".ui-tabs-nav li" ).map(function( index ) { + var tab = $( this ), + tabIsDisabled = tab.hasClass( "ui-state-disabled" ); + + if ( tabIsDisabled && $.inArray( index, internalState ) !== -1 ) { + return 1; + } + if ( !tabIsDisabled && $.inArray( index, internalState ) === -1 ) { + return 0; + } + // mixed state - invalid + return -1; + }).get(); + + deepEqual( tabs.tabs( "option", "disabled" ), state ); + deepEqual( actual, expected ); + }, + + equalHeight: function( tabs, height ) { + tabs.find( ".ui-tabs-panel" ).each(function() { + equal( $( this ).outerHeight(), height ); + }); + }, + + state: function( tabs ) { + var expected = $.makeArray( arguments ).slice( 1 ), + actual = tabs.find( ".ui-tabs-nav li" ).map(function() { + var tab = $( this ), + panel = $( $.ui.tabs.prototype._sanitizeSelector( + "#" + tab.attr( "aria-controls" ) ) ), + tabIsActive = tab.hasClass( "ui-state-active" ), + panelIsActive = panel.css( "display" ) !== "none"; + + if ( tabIsActive && panelIsActive ) { + return 1; + } + if ( !tabIsActive && !panelIsActive ) { + return 0; + } + return -1; // mixed state - invalid + }).get(); + deepEqual( actual, expected ); + } +}; + diff --git a/apps/it/static/js/ui/tests/unit/testsuite.js b/apps/it/static/js/ui/tests/unit/testsuite.js new file mode 100644 index 0000000..eab1d4e --- /dev/null +++ b/apps/it/static/js/ui/tests/unit/testsuite.js @@ -0,0 +1,298 @@ +(function( $ ) { + +var reset, jshintLoaded; + +window.TestHelpers = {}; + +function includeStyle( url ) { + document.write( "<link rel='stylesheet' href='../../../" + url + "'>" ); +} + +function includeScript( url ) { + document.write( "<script src='../../../" + url + "'></script>" ); +} + +function url( value ) { + return value + (/\?/.test(value) ? "&" : "?") + new Date().getTime() + "" + parseInt(Math.random() * 100000, 10); +} + +reset = QUnit.reset; +QUnit.reset = function() { + // Ensure jQuery events and data on the fixture are properly removed + jQuery("#qunit-fixture").empty(); + // Let QUnit reset the fixture + reset.apply( this, arguments ); +}; + + +QUnit.config.requireExpects = true; + +QUnit.config.urlConfig.push({ + id: "min", + label: "Minified source", + tooltip: "Load minified source files instead of the regular unminified ones." +}); + +TestHelpers.loadResources = QUnit.urlParams.min ? + function() { + includeStyle( "dist/jquery-ui.min.css" ); + includeScript( "dist/jquery-ui.min.js" ); + } : + function( resources ) { + $.each( resources.css || [], function( i, resource ) { + includeStyle( "themes/base/jquery." + resource + ".css" ); + }); + $.each( resources.js || [], function( i, resource ) { + includeScript( resource ); + }); + }; + +QUnit.config.urlConfig.push({ + id: "nojshint", + label: "Skip JSHint", + tooltip: "Skip running JSHint, e.g. within TestSwarm, where Jenkins runs it already" +}); + +jshintLoaded = false; +TestHelpers.testJshint = function( module ) { + if ( QUnit.urlParams.nojshint ) { + return; + } + + if ( !jshintLoaded ) { + includeScript( "external/jshint.js" ); + jshintLoaded = true; + } + + asyncTest( "JSHint", function() { + expect( 1 ); + + $.when( + $.ajax({ + url: url("../../../ui/.jshintrc"), + dataType: "json" + }), + $.ajax({ + url: url("../../../ui/jquery.ui." + module + ".js"), + dataType: "text" + }) + ).done(function( hintArgs, srcArgs ) { + var globals, passed, errors, + jshintrc = hintArgs[ 0 ], + source = srcArgs[ 0 ]; + + globals = jshintrc.globals || {}; + delete jshintrc.globals; + passed = JSHINT( source, jshintrc, globals ); + errors = $.map( JSHINT.errors, function( error ) { + // JSHINT may report null if there are too many errors + if ( !error ) { + return; + } + + return "[L" + error.line + ":C" + error.character + "] " + + error.reason + "\n" + error.evidence + "\n"; + }).join( "\n" ); + ok( passed, errors ); + start(); + }) + .fail(function() { + ok( false, "error loading source" ); + start(); + }); + }); +}; + +function testWidgetDefaults( widget, defaults ) { + var pluginDefaults = $.ui[ widget ].prototype.options; + + // ensure that all defaults have the correct value + test( "defined defaults", function() { + var count = 0; + $.each( defaults, function( key, val ) { + expect( ++count ); + if ( $.isFunction( val ) ) { + ok( $.isFunction( pluginDefaults[ key ] ), key ); + return; + } + deepEqual( pluginDefaults[ key ], val, key ); + }); + }); + + // ensure that all defaults were tested + test( "tested defaults", function() { + var count = 0; + $.each( pluginDefaults, function( key ) { + expect( ++count ); + ok( key in defaults, key ); + }); + }); +} + +function testWidgetOverrides( widget ) { + if ( $.uiBackCompat === false ) { + test( "$.widget overrides", function() { + expect( 4 ); + $.each([ + "_createWidget", + "destroy", + "option", + "_trigger" + ], function( i, method ) { + strictEqual( $.ui[ widget ].prototype[ method ], + $.Widget.prototype[ method ], "should not override " + method ); + }); + }); + } +} + +function testBasicUsage( widget ) { + test( "basic usage", function() { + expect( 3 ); + + var defaultElement = $.ui[ widget ].prototype.defaultElement; + $( defaultElement ).appendTo( "body" )[ widget ]().remove(); + ok( true, "initialized on element" ); + + $( defaultElement )[ widget ]().remove(); + ok( true, "initialized on disconnected DOMElement - never connected" ); + + $( defaultElement ).appendTo( "body" ).remove()[ widget ]().remove(); + ok( true, "initialized on disconnected DOMElement - removed" ); + }); +} + +TestHelpers.commonWidgetTests = function( widget, settings ) { + module( widget + ": common widget" ); + + TestHelpers.testJshint( widget ); + testWidgetDefaults( widget, settings.defaults ); + testWidgetOverrides( widget ); + testBasicUsage( widget ); + test( "version", function() { + expect( 1 ); + ok( "version" in $.ui[ widget ].prototype, "version property exists" ); + }); +}; + +/* + * Taken from https://github.com/jquery/qunit/tree/master/addons/close-enough + */ +window.closeEnough = function( actual, expected, maxDifference, message ) { + var passes = (actual === expected) || Math.abs(actual - expected) <= maxDifference; + QUnit.push(passes, actual, expected, message); +}; + +/* + * Experimental assertion for comparing DOM objects. + * + * Serializes an element and some properties and attributes and it's children if any, otherwise the text. + * Then compares the result using deepEqual. + */ +window.domEqual = function( selector, modifier, message ) { + var expected, actual, + properties = [ + "disabled", + "readOnly" + ], + attributes = [ + "autocomplete", + "aria-activedescendant", + "aria-controls", + "aria-describedby", + "aria-disabled", + "aria-expanded", + "aria-haspopup", + "aria-hidden", + "aria-labelledby", + "aria-pressed", + "aria-selected", + "aria-valuemax", + "aria-valuemin", + "aria-valuenow", + "class", + "href", + "id", + "nodeName", + "role", + "tabIndex", + "title" + ]; + + function getElementStyles( elem ) { + var key, len, + style = elem.ownerDocument.defaultView ? + elem.ownerDocument.defaultView.getComputedStyle( elem, null ) : + elem.currentStyle, + styles = {}; + + if ( style && style.length && style[ 0 ] && style[ style[ 0 ] ] ) { + len = style.length; + while ( len-- ) { + key = style[ len ]; + if ( typeof style[ key ] === "string" ) { + styles[ $.camelCase( key ) ] = style[ key ]; + } + } + // support: Opera, IE <9 + } else { + for ( key in style ) { + if ( typeof style[ key ] === "string" ) { + styles[ key ] = style[ key ]; + } + } + } + + return styles; + } + + function extract( elem ) { + if ( !elem || !elem.length ) { + QUnit.push( false, actual, expected, + "domEqual failed, can't extract " + selector + ", message was: " + message ); + return; + } + + var children, + result = {}; + $.each( properties, function( index, attr ) { + var value = elem.prop( attr ); + result[ attr ] = value !== undefined ? value : ""; + }); + $.each( attributes, function( index, attr ) { + var value = elem.attr( attr ); + result[ attr ] = value !== undefined ? value : ""; + }); + result.style = getElementStyles( elem[ 0 ] ); + result.events = $._data( elem[ 0 ], "events" ); + result.data = $.extend( {}, elem.data() ); + delete result.data[ $.expando ]; + children = elem.children(); + if ( children.length ) { + result.children = elem.children().map(function() { + return extract( $( this ) ); + }).get(); + } else { + result.text = elem.text(); + } + return result; + } + + function done() { + actual = extract( $( selector ) ); + QUnit.push( QUnit.equiv(actual, expected), actual, expected, message ); + } + + // Get current state prior to modifier + expected = extract( $( selector ) ); + + // Run modifier (async or sync), then compare state via done() + if ( modifier.length ) { + modifier( done ); + } else { + modifier(); + done(); + } +}; + +}( jQuery )); diff --git a/apps/it/static/js/ui/tests/unit/tooltip/all.html b/apps/it/static/js/ui/tests/unit/tooltip/all.html new file mode 100644 index 0000000..614b7f7 --- /dev/null +++ b/apps/it/static/js/ui/tests/unit/tooltip/all.html @@ -0,0 +1,30 @@ +<!doctype html> +<html lang="en"> +<head> + <meta charset="utf-8"> + <title>jQuery UI Tooltip Test Suite</title> + + <script src="../../../jquery-1.10.2.js"></script> + + <link rel="stylesheet" href="../../../external/qunit.css"> + <link rel="stylesheet" href="../qunit-composite.css"> + <script src="../../../external/qunit.js"></script> + <script src="../qunit-composite.js"></script> + <script src="../subsuite.js"></script> + + <script> + testAllVersions( "tooltip" ); + </script> +</head> +<body> + +<h1 id="qunit-header">jQuery UI Tooltip Test Suite</h1> +<h2 id="qunit-banner"></h2> +<div id="qunit-testrunner-toolbar"></div> +<h2 id="qunit-userAgent"></h2> +<ol id="qunit-tests"></ol> +<div id="qunit-fixture"> + +</div> +</body> +</html> diff --git a/apps/it/static/js/ui/tests/unit/tooltip/tooltip.html b/apps/it/static/js/ui/tests/unit/tooltip/tooltip.html new file mode 100644 index 0000000..ec616be --- /dev/null +++ b/apps/it/static/js/ui/tests/unit/tooltip/tooltip.html @@ -0,0 +1,55 @@ +<!doctype html> +<html lang="en"> +<head> + <meta charset="utf-8"> + <title>jQuery UI Tooltip Test Suite</title> + + <script src="../../jquery.js"></script> + <link rel="stylesheet" href="../../../external/qunit.css"> + <script src="../../../external/qunit.js"></script> + <script src="../../jquery.simulate.js"></script> + <script src="../testsuite.js"></script> + <script> + TestHelpers.loadResources({ + css: [ "ui.core", "ui.tooltip" ], + js: [ + "ui/jquery.ui.core.js", + "ui/jquery.ui.widget.js", + "ui/jquery.ui.position.js", + "ui/jquery.ui.tooltip.js" + ] + }); + </script> + + <script src="tooltip_common.js"></script> + <script src="tooltip_core.js"></script> + <script src="tooltip_events.js"></script> + <script src="tooltip_methods.js"></script> + <script src="tooltip_options.js"></script> + + <script src="../swarminject.js"></script> +</head> +<body> + +<h1 id="qunit-header">jQuery UI Tooltip Test Suite</h1> +<h2 id="qunit-banner"></h2> +<div id="qunit-testrunner-toolbar"></div> +<h2 id="qunit-userAgent"></h2> +<ol id="qunit-tests"></ol> +<div id="qunit-fixture"> + +<div> + <a id="tooltipped1" href="#" title="anchortitle">anchor</a> + <input title="inputtitle"> + <span id="multiple-describedby" aria-describedby="fixture-span" title="...">aria-describedby</span> + <span id="fixture-span" title="title-text">span</span> + <span id="contains-tooltipped" title="parent"><span id="contained-tooltipped" title="child">baz</span></span> +</div> + +<form id="tooltip-form"> + <input name="title" title="attroperties"> +</form> + +</div> +</body> +</html> diff --git a/apps/it/static/js/ui/tests/unit/tooltip/tooltip_common.js b/apps/it/static/js/ui/tests/unit/tooltip/tooltip_common.js new file mode 100644 index 0000000..2d6ea91 --- /dev/null +++ b/apps/it/static/js/ui/tests/unit/tooltip/tooltip_common.js @@ -0,0 +1,21 @@ +TestHelpers.commonWidgetTests( "tooltip", { + defaults: { + content: function() {}, + disabled: false, + hide: true, + items: "[title]:not([disabled])", + position: { + my: "left top+15", + at: "left bottom", + collision: "flipfit flip" + }, + show: true, + tooltipClass: null, + track: false, + + // callbacks + close: null, + create: null, + open: null + } +}); diff --git a/apps/it/static/js/ui/tests/unit/tooltip/tooltip_core.js b/apps/it/static/js/ui/tests/unit/tooltip/tooltip_core.js new file mode 100644 index 0000000..c3568bf --- /dev/null +++ b/apps/it/static/js/ui/tests/unit/tooltip/tooltip_core.js @@ -0,0 +1,137 @@ +(function( $ ) { + +module( "tooltip: core" ); + +test( "markup structure", function() { + expect( 7 ); + var element = $( "#tooltipped1" ).tooltip(), + tooltip = $( ".ui-tooltip" ); + + equal( element.attr( "aria-describedby" ), undefined, "no aria-describedby on init" ); + equal( tooltip.length, 0, "no tooltip on init" ); + + element.tooltip( "open" ); + tooltip = $( "#" + element.data( "ui-tooltip-id" ) ); + equal( tooltip.length, 1, "tooltip exists" ); + equal( element.attr( "aria-describedby"), tooltip.attr( "id" ), "aria-describedby" ); + ok( tooltip.hasClass( "ui-tooltip" ), "tooltip is .ui-tooltip" ); + equal( tooltip.length, 1, ".ui-tooltip exists" ); + equal( tooltip.find( ".ui-tooltip-content" ).length, 1, + ".ui-tooltip-content exists" ); +}); + +test( "accessibility", function() { + expect( 5 ); + + var tooltipId, + tooltip, + element = $( "#multiple-describedby" ).tooltip(); + + element.tooltip( "open" ); + tooltipId = element.data( "ui-tooltip-id" ); + tooltip = $( "#" + tooltipId ); + equal( tooltip.attr( "role" ), "tooltip", "role" ); + equal( element.attr( "aria-describedby" ), "fixture-span " + tooltipId, + "multiple describedby when open" ); + // strictEqual to distinguish between .removeAttr( "title" ) and .attr( "title", "" ) + // support: jQuery <1.6.2 + // support: IE <8 + // We should use strictEqual( ..., undefined ) when dropping jQuery 1.6.1 support (or IE6/7) + ok( !element.attr( "title" ), "no title when open" ); + element.tooltip( "close" ); + equal( element.attr( "aria-describedby" ), "fixture-span", + "correct describedby when closed" ); + equal( element.attr( "title" ), "...", "title restored when closed" ); +}); + +test( "delegated removal", function() { + expect( 2 ); + + var container = $( "#contains-tooltipped" ).tooltip(), + element = $( "#contained-tooltipped" ); + + element.trigger( "mouseover" ); + equal( $( ".ui-tooltip" ).length, 1 ); + + container.empty(); + equal( $( ".ui-tooltip" ).length, 0 ); +}); + +test( "nested tooltips", function() { + expect( 2 ); + + var child = $( "#contained-tooltipped" ), + parent = $( "#contains-tooltipped" ).tooltip({ + show: null, + hide: null + }); + + parent.trigger( "mouseover" ); + equal( $( ".ui-tooltip:visible" ).text(), "parent" ); + + child.trigger( "mouseover" ); + equal( $( ".ui-tooltip" ).text(), "child" ); +}); + +// #8742 +test( "form containing an input with name title", function() { + expect( 4 ); + + var form = $( "#tooltip-form" ).tooltip({ + show: null, + hide: null + }), + input = form.find( "[name=title]" ); + + equal( $( ".ui-tooltip" ).length, 0, "no tooltips on init" ); + + input.trigger( "mouseover" ); + equal( $( ".ui-tooltip" ).length, 1, "tooltip for input" ); + input.trigger( "mouseleave" ); + equal( $( ".ui-tooltip" ).length, 0, "tooltip for input closed" ); + + form.trigger( "mouseover" ); + equal( $( ".ui-tooltip" ).length, 0, "no tooltip for form" ); +}); + +test( "tooltip on .ui-state-disabled element", function() { + expect( 2 ); + + var container = $( "#contains-tooltipped" ).tooltip(), + element = $( "#contained-tooltipped" ).addClass( "ui-state-disabled" ); + + element.trigger( "mouseover" ); + equal( $( ".ui-tooltip" ).length, 1 ); + + container.empty(); + equal( $( ".ui-tooltip" ).length, 0 ); +}); + +// http://bugs.jqueryui.com/ticket/8740 +asyncTest( "programmatic focus with async content", function() { + expect( 2 ); + var element = $( "#tooltipped1" ).tooltip({ + content: function( response ) { + setTimeout(function() { + response( "test" ); + }); + } + }); + + element.bind( "tooltipopen", function( event ) { + deepEqual( event.originalEvent.type, "focusin" ); + + element.bind( "tooltipclose", function( event ) { + deepEqual( event.originalEvent.type, "focusout" ); + start(); + }); + + setTimeout(function() { + element.blur(); + }); + }); + + element.focus(); +}); + +}( jQuery ) ); diff --git a/apps/it/static/js/ui/tests/unit/tooltip/tooltip_events.js b/apps/it/static/js/ui/tests/unit/tooltip/tooltip_events.js new file mode 100644 index 0000000..de16471 --- /dev/null +++ b/apps/it/static/js/ui/tests/unit/tooltip/tooltip_events.js @@ -0,0 +1,57 @@ +(function( $ ) { + +module( "tooltip: events" ); + +test( "programmatic triggers", function() { + expect( 4 ); + var tooltip, + element = $( "#tooltipped1" ).tooltip(); + + element.one( "tooltipopen", function( event, ui ) { + tooltip = ui.tooltip; + ok( !( "originalEvent" in event ), "open" ); + strictEqual( ui.tooltip[0], + $( "#" + element.data( "ui-tooltip-id" ) )[0], "ui.tooltip" ); + }); + element.tooltip( "open" ); + + element.one( "tooltipclose", function( event, ui ) { + ok( !( "originalEvent" in event ), "close" ); + strictEqual( ui.tooltip[0], tooltip[0], "ui.tooltip" ); + }); + element.tooltip( "close" ); +}); + +test( "mouse events", function() { + expect( 2 ); + var element = $( "#tooltipped1" ).tooltip(); + + element.bind( "tooltipopen", function( event ) { + deepEqual( event.originalEvent.type, "mouseover" ); + }); + element.trigger( "mouseover" ); + + element.bind( "tooltipclose", function( event ) { + deepEqual( event.originalEvent.type, "mouseleave" ); + }); + element.trigger( "focusout" ); + element.trigger( "mouseleave" ); +}); + +test( "focus events", function() { + expect( 2 ); + var element = $( "#tooltipped1" ).tooltip(); + + element.bind( "tooltipopen", function( event ) { + deepEqual( event.originalEvent.type, "focusin" ); + }); + element.trigger( "focusin" ); + + element.bind( "tooltipclose", function( event ) { + deepEqual( event.originalEvent.type, "focusout" ); + }); + element.trigger( "mouseleave" ); + element.trigger( "focusout" ); +}); + +}( jQuery ) ); diff --git a/apps/it/static/js/ui/tests/unit/tooltip/tooltip_methods.js b/apps/it/static/js/ui/tests/unit/tooltip/tooltip_methods.js new file mode 100644 index 0000000..c846d21 --- /dev/null +++ b/apps/it/static/js/ui/tests/unit/tooltip/tooltip_methods.js @@ -0,0 +1,94 @@ +(function( $ ) { + +module( "tooltip: methods" ); + +test( "destroy", function() { + expect( 3 ); + var element = $( "#tooltipped1" ); + + domEqual( "#tooltipped1", function() { + element.tooltip().tooltip( "destroy" ); + }); + + // make sure that open tooltips are removed on destroy + domEqual( "#tooltipped1", function() { + element + .tooltip() + .tooltip( "open", $.Event( "mouseover", { target: element[0] }) ) + .tooltip( "destroy" ); + }); + equal( $( ".ui-tooltip" ).length, 0 ); +}); + +test( "open/close", function() { + expect( 3 ); + $.fx.off = true; + var tooltip, + element = $( "#tooltipped1" ).tooltip(); + equal( $( ".ui-tooltip" ).length, 0, "no tooltip on init" ); + + element.tooltip( "open" ); + tooltip = $( "#" + element.data( "ui-tooltip-id" ) ); + ok( tooltip.is( ":visible" ) ); + + element.tooltip( "close" ); + ok( tooltip.is( ":hidden" ) ); + $.fx.off = false; +}); + +// #8626 - Calling open() without an event +test( "open/close with tracking", function() { + expect( 3 ); + $.fx.off = true; + var tooltip, + element = $( "#tooltipped1" ).tooltip({ track: true }); + equal( $( ".ui-tooltip" ).length, 0, "no tooltip on init" ); + + element.tooltip( "open" ); + tooltip = $( "#" + element.data( "ui-tooltip-id" ) ); + ok( tooltip.is( ":visible" ) ); + + element.tooltip( "close" ); + ok( tooltip.is( ":hidden" ) ); + $.fx.off = false; +}); + +test( "enable/disable", function() { + expect( 7 ); + $.fx.off = true; + var tooltip, + element = $( "#tooltipped1" ).tooltip(); + equal( $( ".ui-tooltip" ).length, 0, "no tooltip on init" ); + + element.tooltip( "open" ); + tooltip = $( "#" + element.data( "ui-tooltip-id" ) ); + ok( tooltip.is( ":visible" ) ); + + element.tooltip( "disable" ); + equal( $( ".ui-tooltip" ).length, 0, "no tooltip when disabled" ); + // support: jQuery <1.6.2 + // support: IE <8 + // We should use strictEqual( ..., undefined ) when dropping jQuery 1.6.1 support (or IE6/7) + ok( !tooltip.attr( "title" ), "title removed on disable" ); + + element.tooltip( "open" ); + equal( $( ".ui-tooltip" ).length, 0, "open does nothing when disabled" ); + + element.tooltip( "enable" ); + equal( element.attr( "title" ), "anchortitle", "title restored on enable" ); + + element.tooltip( "open" ); + tooltip = $( "#" + element.data( "ui-tooltip-id" ) ); + ok( tooltip.is( ":visible" ) ); + $.fx.off = false; +}); + +test( "widget", function() { + expect( 2 ); + var element = $( "#tooltipped1" ).tooltip(), + widgetElement = element.tooltip( "widget" ); + equal( widgetElement.length, 1, "one element" ); + strictEqual( widgetElement[ 0 ], element[ 0 ], "same element" ); +}); + +}( jQuery ) ); diff --git a/apps/it/static/js/ui/tests/unit/tooltip/tooltip_options.js b/apps/it/static/js/ui/tests/unit/tooltip/tooltip_options.js new file mode 100644 index 0000000..01ac250 --- /dev/null +++ b/apps/it/static/js/ui/tests/unit/tooltip/tooltip_options.js @@ -0,0 +1,171 @@ +(function( $ ) { + +module( "tooltip: options" ); + +test( "disabled: true", function() { + expect( 1 ); + $( "#tooltipped1" ).tooltip({ + disabled: true + }).tooltip( "open" ); + equal( $( ".ui-tooltip" ).length, 0 ); +}); + +test( "content: default", function() { + expect( 1 ); + var element = $( "#tooltipped1" ).tooltip().tooltip( "open" ); + deepEqual( $( "#" + element.data( "ui-tooltip-id" ) ).text(), "anchortitle" ); +}); + +test( "content: default; HTML escaping", function() { + expect( 2 ); + var scriptText = "<script>$.ui.tooltip.hacked = true;</script>", + element = $( "#tooltipped1" ); + + $.ui.tooltip.hacked = false; + element.attr( "title", scriptText ) + .tooltip() + .tooltip( "open" ); + equal( $.ui.tooltip.hacked, false, "script did not execute" ); + deepEqual( $( "#" + element.data( "ui-tooltip-id" ) ).text(), scriptText, + "correct tooltip text" ); +}); + +test( "content: return string", function() { + expect( 1 ); + var element = $( "#tooltipped1" ).tooltip({ + content: function() { + return "customstring"; + } + }).tooltip( "open" ); + deepEqual( $( "#" + element.data( "ui-tooltip-id" ) ).text(), "customstring" ); +}); + +test( "content: return jQuery", function() { + expect( 1 ); + var element = $( "#tooltipped1" ).tooltip({ + content: function() { + return $( "<div>" ).html( "cu<b>s</b>tomstring" ); + } + }).tooltip( "open" ); + deepEqual( $( "#" + element.data( "ui-tooltip-id" ) ).text(), "customstring" ); +}); + +asyncTest( "content: sync + async callback", function() { + expect( 2 ); + var element = $( "#tooltipped1" ).tooltip({ + content: function( response ) { + setTimeout(function() { + deepEqual( $( "#" + element.data("ui-tooltip-id") ).text(), "loading..." ); + + response( "customstring2" ); + setTimeout(function() { + deepEqual( $( "#" + element.data("ui-tooltip-id") ).text(), "customstring2" ); + start(); + }, 13 ); + }, 13 ); + return "loading..."; + } + }).tooltip( "open" ); +}); + +test( "content: change while open", function() { + expect( 2 ) ; + var element = $( "#tooltipped1" ).tooltip({ + content: function() { + return "old"; + } + }); + + element.one( "tooltipopen", function( event, ui ) { + equal( ui.tooltip.text(), "old", "original content" ); + element.tooltip( "option", "content", function() { + return "new"; + }); + equal( ui.tooltip.text(), "new", "updated content" ); + }); + + element.tooltip( "open" ); +}); + +test( "content: string", function() { + expect( 1 ); + $( "#tooltipped1" ).tooltip({ + content: "just a string", + open: function( event, ui ) { + equal( ui.tooltip.text(), "just a string" ); + } + }).tooltip( "open" ); +}); + +test( "items", function() { + expect( 2 ); + var event, + element = $( "#qunit-fixture" ).tooltip({ + items: "#fixture-span" + }); + + event = $.Event( "mouseenter" ); + event.target = $( "#fixture-span" )[ 0 ]; + element.tooltip( "open", event ); + deepEqual( $( "#" + $( "#fixture-span" ).data( "ui-tooltip-id" ) ).text(), "title-text" ); + + // make sure default [title] doesn't get used + event.target = $( "#tooltipped1" )[ 0 ]; + element.tooltip( "open", event ); + deepEqual( $( "#tooltipped1" ).data( "ui-tooltip-id" ), undefined ); + + element.tooltip( "destroy" ); +}); + +test( "tooltipClass", function() { + expect( 1 ); + var element = $( "#tooltipped1" ).tooltip({ + tooltipClass: "custom" + }).tooltip( "open" ); + ok( $( "#" + element.data( "ui-tooltip-id" ) ).hasClass( "custom" ) ); +}); + +test( "track + show delay", function() { + expect( 2 ); + var event, + leftVal = 314, + topVal = 159, + offsetVal = 26, + element = $( "#tooltipped1" ).tooltip({ + track: true, + show: { + delay: 1 + }, + position: { + my: "left+" + offsetVal + " top+" + offsetVal, + at: "right bottom" + } + }); + + event = $.Event( "mouseover" ); + event.target = $( "#tooltipped1" )[ 0 ]; + event.originalEvent = { type: "mouseover" }; + event.pageX = leftVal; + event.pageY = topVal; + element.trigger( event ); + + event = $.Event( "mousemove" ); + event.target = $( "#tooltipped1" )[ 0 ]; + event.originalEvent = { type: "mousemove" }; + event.pageX = leftVal; + event.pageY = topVal; + element.trigger( event ); + + equal( $( ".ui-tooltip" ).css( "left" ), leftVal + offsetVal + "px" ); + equal( $( ".ui-tooltip" ).css( "top" ), topVal + offsetVal + "px" ); +}); + +test( "track and programmatic focus", function() { + expect( 1 ); + $( "#qunit-fixture div input" ).tooltip({ + track: true + }).focus(); + equal( "inputtitle", $( ".ui-tooltip" ).text() ); +}); + +}( jQuery ) ); diff --git a/apps/it/static/js/ui/tests/unit/widget/all.html b/apps/it/static/js/ui/tests/unit/widget/all.html new file mode 100644 index 0000000..ab493ab --- /dev/null +++ b/apps/it/static/js/ui/tests/unit/widget/all.html @@ -0,0 +1,30 @@ +<!doctype html> +<html lang="en"> +<head> + <meta charset="utf-8"> + <title>jQuery UI Widget Test Suite</title> + + <script src="../../../jquery-1.10.2.js"></script> + + <link rel="stylesheet" href="../../../external/qunit.css"> + <link rel="stylesheet" href="../qunit-composite.css"> + <script src="../../../external/qunit.js"></script> + <script src="../qunit-composite.js"></script> + <script src="../subsuite.js"></script> + + <script> + testAllVersions( "widget" ); + </script> +</head> +<body> + +<h1 id="qunit-header">jQuery UI Widget Test Suite</h1> +<h2 id="qunit-banner"></h2> +<div id="qunit-testrunner-toolbar"></div> +<h2 id="qunit-userAgent"></h2> +<ol id="qunit-tests"></ol> +<div id="qunit-fixture"> + +</div> +</body> +</html> diff --git a/apps/it/static/js/ui/tests/unit/widget/widget.html b/apps/it/static/js/ui/tests/unit/widget/widget.html new file mode 100644 index 0000000..15eaee6 --- /dev/null +++ b/apps/it/static/js/ui/tests/unit/widget/widget.html @@ -0,0 +1,44 @@ +<!DOCTYPE html> +<html lang="en"> +<head> + <meta charset="utf-8"> + <title>jQuery UI Widget Test Suite</title> + + <script src="../../jquery.js"></script> + <link rel="stylesheet" href="../../../external/qunit.css"> + <script src="../../../external/qunit.js"></script> + <script src="../../jquery.simulate.js"></script> + <script src="../testsuite.js"></script> + <script> + TestHelpers.loadResources({ + css: [ "ui.core" ], + js: [ + "ui/jquery.ui.widget.js" + ] + }); + </script> + + <script src="widget_core.js"></script> + <script src="widget_extend.js"></script> + <script src="widget_animation.js"></script> + + <script src="../swarminject.js"></script> +</head> +<body> + +<h1 id="qunit-header">jQuery UI Widget Test Suite</h1> +<h2 id="qunit-banner"></h2> +<div id="qunit-testrunner-toolbar"></div> +<h2 id="qunit-userAgent"></h2> +<ol id="qunit-tests"></ol> +<div id="qunit-fixture"> + +<div id="widget-wrapper"> + <div id="widget"> + <div>...</div> + </div> +</div> + +</div> +</body> +</html> diff --git a/apps/it/static/js/ui/tests/unit/widget/widget_animation.js b/apps/it/static/js/ui/tests/unit/widget/widget_animation.js new file mode 100644 index 0000000..8ef55aa --- /dev/null +++ b/apps/it/static/js/ui/tests/unit/widget/widget_animation.js @@ -0,0 +1,257 @@ + +module( "widget animation", (function() { + var show = $.fn.show, + fadeIn = $.fn.fadeIn, + slideDown = $.fn.slideDown; + return { + setup: function() { + $.widget( "ui.testWidget", { + _create: function() { + this.element.hide(); + }, + show: function( fn ) { + this._show( this.element, this.options.show, fn ); + } + }); + $.effects = { effect: { testEffect: $.noop } }; + }, + teardown: function() { + delete $.ui.testWidget; + delete $.effects.effect.testEffect; + $.fn.show = show; + $.fn.fadeIn = fadeIn; + $.fn.slideDown = slideDown; + } + }; +}())); + +asyncTest( "show: null", function() { + expect( 4 ); + + var element = $( "#widget" ).testWidget(), + hasRun = false; + $.fn.show = function() { + ok( true, "show called" ); + equal( arguments.length, 0, "no args passed to show" ); + }; + + element + .delay( 50 ) + .queue(function( next ) { + ok( !hasRun, "queue before show" ); + next(); + }) + .testWidget( "show", function() { + hasRun = true; + }) + .queue(function( next ) { + ok( hasRun, "queue after show" ); + start(); + next(); + }); +}); + +asyncTest( "show: true", function() { + expect( 4 ); + + var element = $( "#widget" ).testWidget({ + show: true + }), + hasRun = false; + $.fn.fadeIn = function( duration, easing, complete ) { + return this.queue(function( next ) { + strictEqual( duration, undefined, "duration" ); + strictEqual( easing, undefined, "easing" ); + complete(); + next(); + }); + }; + + element + .delay( 50 ) + .queue(function( next ) { + ok( !hasRun, "queue before show" ); + next(); + }) + .testWidget( "show", function() { + hasRun = true; + }) + .queue(function( next ) { + ok( hasRun, "queue after show" ); + start(); + next(); + }); +}); + +asyncTest( "show: number", function() { + expect( 4 ); + + var element = $( "#widget" ).testWidget({ + show: 123 + }), + hasRun = false; + $.fn.fadeIn = function( duration, easing, complete ) { + return this.queue(function( next ) { + strictEqual( duration, 123, "duration" ); + strictEqual( easing, undefined, "easing" ); + complete(); + next(); + }); + }; + + element + .delay( 50 ) + .queue(function( next ) { + ok( !hasRun, "queue before show" ); + next(); + }) + .testWidget( "show", function() { + hasRun = true; + }) + .queue(function( next ) { + ok( hasRun, "queue after show" ); + start(); + next(); + }); +}); + +asyncTest( "show: core animation", function() { + expect( 4 ); + + var element = $( "#widget" ).testWidget({ + show: "slideDown" + }), + hasRun = false; + $.fn.slideDown = function( duration, easing, complete ) { + return this.queue(function( next ) { + strictEqual( duration, undefined, "duration" ); + strictEqual( easing, undefined, "easing" ); + complete(); + next(); + }); + }; + + element + .delay( 50 ) + .queue(function( next ) { + ok( !hasRun, "queue before show" ); + next(); + }) + .testWidget( "show", function() { + hasRun = true; + }) + .queue(function( next ) { + ok( hasRun, "queue after show" ); + start(); + next(); + }); +}); + +asyncTest( "show: effect", function() { + expect( 5 ); + + var element = $( "#widget" ).testWidget({ + show: "testEffect" + }), + hasRun = false; + $.fn.show = function( options ) { + return this.queue(function( next ) { + equal( options.effect, "testEffect", "effect" ); + ok( !("duration" in options), "duration" ); + ok( !("easing" in options), "easing" ); + options.complete(); + next(); + }); + }; + + element + .delay( 50 ) + .queue(function( next ) { + ok( !hasRun, "queue before show" ); + next(); + }) + .testWidget( "show", function() { + hasRun = true; + }) + .queue(function( next ) { + ok( hasRun, "queue after show" ); + start(); + next(); + }); +}); + +asyncTest( "show: object(core animation)", function() { + expect( 4 ); + + var element = $( "#widget" ).testWidget({ + show: { + effect: "slideDown", + duration: 123, + easing: "testEasing" + } + }), + hasRun = false; + $.fn.slideDown = function( duration, easing, complete ) { + return this.queue(function( next ) { + equal( duration, 123, "duration" ); + equal( easing, "testEasing", "easing" ); + complete(); + next(); + }); + }; + + element + .delay( 50 ) + .queue(function( next ) { + ok( !hasRun, "queue before show" ); + next(); + }) + .testWidget( "show", function() { + hasRun = true; + }) + .queue(function( next ) { + ok( hasRun, "queue after show" ); + start(); + next(); + }); +}); + +asyncTest( "show: object(effect)", function() { + expect( 3 ); + + var element = $( "#widget" ).testWidget({ + show: { + effect: "testEffect", + duration: 123, + easing: "testEasing" + } + }), + hasRun = false; + $.fn.show = function( options ) { + return this.queue(function( next ) { + deepEqual( options, { + effect: "testEffect", + duration: 123, + easing: "testEasing", + complete: options.complete + }); + options.complete(); + next(); + }); + }; + + element + .delay( 50 ) + .queue(function( next ) { + ok( !hasRun, "queue before show" ); + next(); + }) + .testWidget( "show", function() { + hasRun = true; + }) + .queue(function( next ) { + ok( hasRun, "queue after show" ); + start(); + next(); + }); +}); diff --git a/apps/it/static/js/ui/tests/unit/widget/widget_core.js b/apps/it/static/js/ui/tests/unit/widget/widget_core.js new file mode 100644 index 0000000..7f9145c --- /dev/null +++ b/apps/it/static/js/ui/tests/unit/widget/widget_core.js @@ -0,0 +1,1459 @@ +(function( $ ) { + +module( "widget factory", { + teardown: function() { + if ( $.ui ) { + delete $.ui.testWidget; + delete $.fn.testWidget; + } + } +}); + +TestHelpers.testJshint( "widget" ); + +test( "widget creation", function() { + expect( 5 ); + var method, + myPrototype = { + _create: function() { + equal( method, "_create", "create function is copied over" ); + }, + creationTest: function() { + equal( method, "creationTest", "random function is copied over" ); + } + }; + + $.widget( "ui.testWidget", myPrototype ); + ok( $.isFunction( $.ui.testWidget ), "constructor was created" ); + equal( typeof $.ui.testWidget.prototype, "object", "prototype was created" ); + method = "_create"; + $.ui.testWidget.prototype._create(); + method = "creationTest"; + $.ui.testWidget.prototype.creationTest(); + equal( $.ui.testWidget.prototype.option, $.Widget.prototype.option, + "option method copied over from base widget" ); +}); + +test( "element normalization", function() { + expect( 11 ); + var elem; + $.widget( "ui.testWidget", {} ); + + $.ui.testWidget.prototype._create = function() { + // workaround for core ticket #8381 + this.element.appendTo( "#qunit-fixture" ); + ok( this.element.is( "div" ), "generated div" ); + deepEqual( this.element.data( "ui-testWidget" ), this, "instance stored in .data()" ); + }; + $.ui.testWidget(); + + $.ui.testWidget.prototype.defaultElement = "<span data-test='pass'></span>"; + $.ui.testWidget.prototype._create = function() { + ok( this.element.is( "span[data-test=pass]" ), "generated span with properties" ); + deepEqual( this.element.data( "ui-testWidget" ), this, "instace stored in .data()" ); + }; + $.ui.testWidget(); + + elem = $( "<input>" ); + $.ui.testWidget.prototype._create = function() { + deepEqual( this.element[ 0 ], elem[ 0 ], "from element" ); + deepEqual( elem.data( "ui-testWidget" ), this, "instace stored in .data()" ); + }; + $.ui.testWidget( {}, elem[ 0 ] ); + + elem = $( "<div>" ); + $.ui.testWidget.prototype._create = function() { + deepEqual( this.element[ 0 ], elem[ 0 ], "from jQuery object" ); + deepEqual( elem.data( "ui-testWidget" ), this, "instace stored in .data()" ); + }; + $.ui.testWidget( {}, elem ); + + elem = $( "<div id='element-normalization-selector'></div>" ) + .appendTo( "#qunit-fixture" ); + $.ui.testWidget.prototype._create = function() { + deepEqual( this.element[ 0 ], elem[ 0 ], "from selector" ); + deepEqual( elem.data( "ui-testWidget" ), this, "instace stored in .data()" ); + }; + $.ui.testWidget( {}, "#element-normalization-selector" ); + + $.ui.testWidget.prototype.defaultElement = null; + $.ui.testWidget.prototype._create = function() { + // using strictEqual throws an error (Maximum call stack size exceeded) + ok( this.element[ 0 ] === this, "instance as element" ); + }; + $.ui.testWidget(); +}); + +test( "custom selector expression", function() { + expect( 1 ); + var elem = $( "<div>" ).appendTo( "#qunit-fixture" ); + $.widget( "ui.testWidget", {} ); + elem.testWidget(); + deepEqual( $( ":ui-testwidget" )[0], elem[0] ); + elem.testWidget( "destroy" ); +}); + +test( "jQuery usage", function() { + expect( 14 ); + + var elem, instance, ret, + shouldCreate = false; + + $.widget( "ui.testWidget", { + getterSetterVal: 5, + _create: function() { + ok( shouldCreate, "create called on instantiation" ); + }, + methodWithParams: function( param1, param2 ) { + ok( true, "method called via .pluginName(methodName)" ); + equal( param1, "value1", + "parameter passed via .pluginName(methodName, param)" ); + equal( param2, "value2", + "multiple parameters passed via .pluginName(methodName, param, param)" ); + + return this; + }, + getterSetterMethod: function( val ) { + if ( val ) { + this.getterSetterVal = val; + } else { + return this.getterSetterVal; + } + }, + jQueryObject: function() { + return $( "body" ); + } + }); + + shouldCreate = true; + elem = $( "<div>" ) + .bind( "testwidgetcreate", function() { + ok( shouldCreate, "create event triggered on instantiation" ); + }) + .testWidget(); + shouldCreate = false; + + instance = elem.data( "ui-testWidget" ); + equal( typeof instance, "object", "instance stored in .data(pluginName)" ); + equal( instance.element[0], elem[0], "element stored on widget" ); + ret = elem.testWidget( "methodWithParams", "value1", "value2" ); + equal( ret, elem, "jQuery object returned from method call" ); + + ret = elem.testWidget( "getterSetterMethod" ); + equal( ret, 5, "getter/setter can act as getter" ); + ret = elem.testWidget( "getterSetterMethod", 30 ); + equal( ret, elem, "getter/setter method can be chainable" ); + equal( instance.getterSetterVal, 30, "getter/setter can act as setter" ); + ret = elem.testWidget( "jQueryObject" ); + equal( ret[ 0 ], document.body, "returned jQuery object" ); + equal( ret.end(), elem, "stack preserved" ); + + elem.testWidget( "destroy" ); + equal( elem.data( "ui-testWidget" ), null ); +}); + +test( "direct usage", function() { + expect( 9 ); + + var elem, instance, ret, + shouldCreate = false; + + $.widget( "ui.testWidget", { + getterSetterVal: 5, + _create: function() { + ok( shouldCreate, "create called on instantiation" ); + }, + methodWithParams: function( param1, param2 ) { + ok( true, "method called dirctly" ); + equal( param1, "value1", "parameter passed via direct call" ); + equal( param2, "value2", "multiple parameters passed via direct call" ); + + return this; + }, + getterSetterMethod: function( val ) { + if ( val ) { + this.getterSetterVal = val; + } else { + return this.getterSetterVal; + } + } + }); + + elem = $( "<div>" )[ 0 ]; + + shouldCreate = true; + instance = new $.ui.testWidget( {}, elem ); + shouldCreate = false; + + equal( $( elem ).data( "ui-testWidget" ), instance, + "instance stored in .data(pluginName)" ); + equal( instance.element[ 0 ], elem, "element stored on widget" ); + + ret = instance.methodWithParams( "value1", "value2" ); + equal( ret, instance, "plugin returned from method call" ); + + ret = instance.getterSetterMethod(); + equal( ret, 5, "getter/setter can act as getter" ); + instance.getterSetterMethod( 30 ); + equal( instance.getterSetterVal, 30, "getter/setter can act as setter" ); +}); + +test( "error handling", function() { + expect( 3 ); + var error = $.error; + $.widget( "ui.testWidget", { + _privateMethod: function () {} + }); + $.error = function( msg ) { + equal( msg, "cannot call methods on testWidget prior to initialization; " + + "attempted to call method 'missing'", "method call before init" ); + }; + $( "<div>" ).testWidget( "missing" ); + $.error = function( msg ) { + equal( msg, "no such method 'missing' for testWidget widget instance", + "invalid method call on widget instance" ); + }; + $( "<div>" ).testWidget().testWidget( "missing" ); + $.error = function ( msg ) { + equal( msg, "no such method '_privateMethod' for testWidget widget instance", + "invalid method call on widget instance" ); + }; + $( "<div>" ).testWidget().testWidget( "_privateMethod" ); + $.error = error; +}); + +test( "merge multiple option arguments", function() { + expect( 1 ); + $.widget( "ui.testWidget", { + _create: function() { + deepEqual( this.options, { + create: null, + disabled: false, + option1: "value1", + option2: "value2", + option3: "value3", + option4: { + option4a: "valuea", + option4b: "valueb" + } + }); + } + }); + $( "<div>" ).testWidget({ + option1: "valuex", + option2: "valuex", + option3: "value3", + option4: { + option4a: "valuex" + } + }, { + option1: "value1", + option2: "value2", + option4: { + option4b: "valueb" + } + }, { + option4: { + option4a: "valuea" + } + }); +}); + +test( "._getCreateOptions()", function() { + expect( 1 ); + $.widget( "ui.testWidget", { + options: { + option1: "valuex", + option2: "valuex", + option3: "value3" + }, + _getCreateOptions: function() { + return { + option1: "override1", + option2: "overideX" + }; + }, + _create: function() { + deepEqual( this.options, { + create: null, + disabled: false, + option1: "override1", + option2: "value2", + option3: "value3" + }); + } + }); + $( "<div>" ).testWidget({ option2: "value2" }); +}); + +test( "._getCreateEventData()", function() { + expect( 1 ); + var data = { foo: "bar" }; + $.widget( "ui.testWidget", { + _getCreateEventData: function() { + return data; + } + }); + $( "<div>" ).testWidget({ + create: function( event, ui ) { + strictEqual( ui, data, "event data" ); + } + }); +}); + +test( "re-init", function() { + expect( 3 ); + var div = $( "<div>" ), + actions = []; + + $.widget( "ui.testWidget", { + _create: function() { + actions.push( "create" ); + }, + _init: function() { + actions.push( "init" ); + }, + _setOption: function( key ) { + actions.push( "option" + key ); + } + }); + + actions = []; + div.testWidget({ foo: "bar" }); + deepEqual( actions, [ "create", "init" ], "correct methods called on init" ); + + actions = []; + div.testWidget(); + deepEqual( actions, [ "init" ], "correct methods call on re-init" ); + + actions = []; + div.testWidget({ foo: "bar" }); + deepEqual( actions, [ "optionfoo", "init" ], "correct methods called on re-init with options" ); +}); + +test( "redeclare", function() { + expect( 2 ); + + $.widget( "ui.testWidget", {} ); + equal( $.ui.testWidget.prototype.widgetEventPrefix, "testWidget" ); + + $.widget( "ui.testWidget", {} ); + equal( $.ui.testWidget.prototype.widgetEventPrefix, "testWidget" ); +}); + +test( "inheritance", function() { + expect( 6 ); + // #5830 - Widget: Using inheritance overwrites the base classes options + $.widget( "ui.testWidgetBase", { + options: { + obj: { + key1: "foo", + key2: "bar" + }, + arr: [ "testing" ] + } + }); + + $.widget( "ui.testWidgetExtension", $.ui.testWidgetBase, { + options: { + obj: { + key1: "baz" + }, + arr: [ "alpha", "beta" ] + } + }); + + equal( $.ui.testWidgetBase.prototype.widgetEventPrefix, "testWidgetBase", + "base class event prefix" ); + deepEqual( $.ui.testWidgetBase.prototype.options.obj, { + key1: "foo", + key2: "bar" + }, "base class option object not overridden"); + deepEqual( $.ui.testWidgetBase.prototype.options.arr, [ "testing" ], + "base class option array not overridden"); + + equal( $.ui.testWidgetExtension.prototype.widgetEventPrefix, "testWidgetExtension", + "extension class event prefix" ); + deepEqual( $.ui.testWidgetExtension.prototype.options.obj, { + key1: "baz", + key2: "bar" + }, "extension class option object extends base"); + deepEqual( $.ui.testWidgetExtension.prototype.options.arr, [ "alpha", "beta" ], + "extension class option array overwrites base"); + + delete $.ui.testWidgetBase; + delete $.ui.testWidgetExtension; +}); + +test( "._super()", function() { + expect( 9 ); + var instance; + $.widget( "ui.testWidget", { + method: function( a, b ) { + deepEqual( this, instance, "this is correct in testWidget" ); + deepEqual( a, 5, "parameter passed to testWidget" ); + deepEqual( b, 20, "second parameter passed to testWidget" ); + return a + b; + } + }); + + $.widget( "ui.testWidget2", $.ui.testWidget, { + method: function( a, b ) { + deepEqual( this, instance, "this is correct in testWidget2" ); + deepEqual( a, 5, "parameter passed to testWidget2" ); + deepEqual( b, 10, "parameter passed to testWidget2" ); + return this._super( a, b*2 ); + } + }); + + $.widget( "ui.testWidget3", $.ui.testWidget2, { + method: function( a ) { + deepEqual( this, instance, "this is correct in testWidget3" ); + deepEqual( a, 5, "parameter passed to testWidget3" ); + var ret = this._super( a, a*2 ); + deepEqual( ret, 25, "super returned value" ); + } + }); + + instance = $( "<div>" ).testWidget3().data( "ui-testWidget3" ); + instance.method( 5 ); + delete $.ui.testWidget3; + delete $.ui.testWidget2; +}); + +test( "._superApply()", function() { + expect( 10 ); + var instance; + $.widget( "ui.testWidget", { + method: function( a, b ) { + deepEqual( this, instance, "this is correct in testWidget" ); + deepEqual( a, 5, "parameter passed to testWidget" ); + deepEqual( b, 10, "second parameter passed to testWidget" ); + return a + b; + } + }); + + $.widget( "ui.testWidget2", $.ui.testWidget, { + method: function( a, b ) { + deepEqual( this, instance, "this is correct in testWidget2" ); + deepEqual( a, 5, "parameter passed to testWidget2" ); + deepEqual( b, 10, "second parameter passed to testWidget2" ); + return this._superApply( arguments ); + } + }); + + $.widget( "ui.testWidget3", $.ui.testWidget2, { + method: function( a, b ) { + deepEqual( this, instance, "this is correct in testWidget3" ); + deepEqual( a, 5, "parameter passed to testWidget3" ); + deepEqual( b, 10, "second parameter passed to testWidget3" ); + var ret = this._superApply( arguments ); + deepEqual( ret, 15, "super returned value" ); + } + }); + + instance = $( "<div>" ).testWidget3().data( "ui-testWidget3" ); + instance.method( 5, 10 ); + delete $.ui.testWidget3; + delete $.ui.testWidget2; +}); + +test( ".option() - getter", function() { + expect( 6 ); + $.widget( "ui.testWidget", { + _create: function() {} + }); + + var options, + div = $( "<div>" ).testWidget({ + foo: "bar", + baz: 5, + qux: [ "quux", "quuux" ] + }); + + deepEqual( div.testWidget( "option", "x" ), null, "non-existent option" ); + deepEqual( div.testWidget( "option", "foo"), "bar", "single option - string" ); + deepEqual( div.testWidget( "option", "baz"), 5, "single option - number" ); + deepEqual( div.testWidget( "option", "qux"), [ "quux", "quuux" ], + "single option - array" ); + + options = div.testWidget( "option" ); + deepEqual( options, { + create: null, + disabled: false, + foo: "bar", + baz: 5, + qux: [ "quux", "quuux" ] + }, "full options hash returned" ); + options.foo = "notbar"; + deepEqual( div.testWidget( "option", "foo"), "bar", + "modifying returned options hash does not modify plugin instance" ); +}); + +test( ".option() - deep option getter", function() { + expect( 5 ); + $.widget( "ui.testWidget", {} ); + var div = $( "<div>" ).testWidget({ + foo: { + bar: "baz", + qux: { + quux: "xyzzy" + } + } + }); + equal( div.testWidget( "option", "foo.bar" ), "baz", "one level deep - string" ); + deepEqual( div.testWidget( "option", "foo.qux" ), { quux: "xyzzy" }, + "one level deep - object" ); + equal( div.testWidget( "option", "foo.qux.quux" ), "xyzzy", "two levels deep - string" ); + equal( div.testWidget( "option", "x.y" ), null, "top level non-existent" ); + equal( div.testWidget( "option", "foo.x.y" ), null, "one level deep - non-existent" ); +}); + +test( ".option() - delegate to ._setOptions()", function() { + expect( 2 ); + var div, + calls = []; + $.widget( "ui.testWidget", { + _create: function() {}, + _setOptions: function( options ) { + calls.push( options ); + } + }); + div = $( "<div>" ).testWidget(); + + calls = []; + div.testWidget( "option", "foo", "bar" ); + deepEqual( calls, [{ foo: "bar" }], "_setOptions called for single option" ); + + calls = []; + div.testWidget( "option", { + bar: "qux", + quux: "quuux" + }); + deepEqual( calls, [{ bar: "qux", quux: "quuux" }], + "_setOptions called with multiple options" ); +}); + +test( ".option() - delegate to ._setOption()", function() { + expect( 3 ); + var div, + calls = []; + $.widget( "ui.testWidget", { + _create: function() {}, + _setOption: function( key, val ) { + calls.push({ + key: key, + val: val + }); + } + }); + div = $( "<div>" ).testWidget(); + + calls = []; + div.testWidget( "option", "foo", "bar" ); + deepEqual( calls, [{ key: "foo", val: "bar" }], + "_setOption called for single option" ); + + calls = []; + div.testWidget( "option", "foo", undefined ); + deepEqual( calls, [{ key: "foo", val: undefined }], + "_setOption called for single option where value is undefined" ); + + calls = []; + div.testWidget( "option", { + bar: "qux", + quux: "quuux" + }); + deepEqual( calls, [ + { key: "bar", val: "qux" }, + { key: "quux", val: "quuux" } + ], "_setOption called with multiple options" ); +}); + +test( ".option() - deep option setter", function() { + expect( 9 ); + $.widget( "ui.testWidget", {} ); + var result, div = $( "<div>" ).testWidget(); + function deepOption( from, to, msg ) { + div.data( "ui-testWidget" ).options.foo = from; + $.ui.testWidget.prototype._setOption = function( key, value ) { + deepEqual( key, "foo", msg + ": key" ); + deepEqual( value, to, msg + ": value" ); + }; + } + + deepOption( { bar: "baz" }, { bar: "qux" }, "one deep" ); + div.testWidget( "option", "foo.bar", "qux" ); + + deepOption( { bar: "baz" }, { bar: undefined }, "one deep - value = undefined" ); + + result = div.testWidget( "option", "foo.bar", undefined ); + + deepEqual ( result, div, "option should return widget on successful set operation" ); + + deepOption( null, { bar: "baz" }, "null" ); + div.testWidget( "option", "foo.bar", "baz" ); + + deepOption( + { bar: "baz", qux: { quux: "quuux" } }, + { bar: "baz", qux: { quux: "quuux", newOpt: "newVal" } }, + "add property" ); + div.testWidget( "option", "foo.qux.newOpt", "newVal" ); +}); + +test( ".enable()", function() { + expect( 2 ); + $.widget( "ui.testWidget", { + _create: function() {}, + _setOption: function( key, val ) { + deepEqual( key, "disabled", "_setOption called with disabled option" ); + deepEqual( val, false, "disabled set to false" ); + } + }); + $( "<div>" ).testWidget().testWidget( "enable" ); +}); + +test( ".disable()", function() { + expect( 2 ); + $.widget( "ui.testWidget", { + _create: function() {}, + _setOption: function( key, val ) { + deepEqual( key, "disabled", "_setOption called with disabled option" ); + deepEqual( val, true, "disabled set to true" ); + } + }); + $( "<div>" ).testWidget().testWidget( "disable" ); +}); + +test( ".widget() - base", function() { + expect( 1 ); + $.widget( "ui.testWidget", { + _create: function() {} + }); + var div = $( "<div>" ).testWidget(); + deepEqual( div[0], div.testWidget( "widget" )[0]); +}); + +test( ".widget() - overriden", function() { + expect( 1 ); + var wrapper = $( "<div>" ); + $.widget( "ui.testWidget", { + _create: function() {}, + widget: function() { + return wrapper; + } + }); + deepEqual( wrapper[0], $( "<div>" ).testWidget().testWidget( "widget" )[0] ); +}); + +test( "._on() to element (default)", function() { + expect( 12 ); + var that, widget; + $.widget( "ui.testWidget", { + _create: function() { + that = this; + this._on({ + keyup: this.keyup, + keydown: "keydown" + }); + }, + keyup: function( event ) { + equal( that, this ); + equal( that.element[0], event.currentTarget ); + equal( "keyup", event.type ); + }, + keydown: function( event ) { + equal( that, this ); + equal( that.element[0], event.currentTarget ); + equal( "keydown", event.type ); + } + }); + widget = $( "<div></div>" ) + .testWidget() + .trigger( "keyup" ) + .trigger( "keydown" ); + widget + .testWidget( "disable" ) + .trigger( "keyup" ) + .trigger( "keydown" ); + widget + .testWidget( "enable" ) + .trigger( "keyup" ) + .trigger( "keydown" ); + widget + .testWidget( "destroy" ) + .trigger( "keyup" ) + .trigger( "keydown" ); +}); + +test( "._on() to element with suppressDisabledCheck", function() { + expect( 18 ); + var that, widget; + $.widget( "ui.testWidget", { + _create: function() { + that = this; + this._on( true, { + keyup: this.keyup, + keydown: "keydown" + }); + }, + keyup: function( event ) { + equal( that, this ); + equal( that.element[0], event.currentTarget ); + equal( "keyup", event.type ); + }, + keydown: function( event ) { + equal( that, this ); + equal( that.element[0], event.currentTarget ); + equal( "keydown", event.type ); + } + }); + widget = $( "<div></div>" ) + .testWidget() + .trigger( "keyup" ) + .trigger( "keydown" ); + widget + .testWidget( "disable" ) + .trigger( "keyup" ) + .trigger( "keydown" ); + widget + .testWidget( "enable" ) + .trigger( "keyup" ) + .trigger( "keydown" ); + widget + .testWidget( "destroy" ) + .trigger( "keyup" ) + .trigger( "keydown" ); +}); + +test( "._on() to descendent", function() { + expect( 12 ); + var that, widget, descendant; + $.widget( "ui.testWidget", { + _create: function() { + that = this; + this._on( this.element.find( "strong" ), { + keyup: this.keyup, + keydown: "keydown" + }); + }, + keyup: function( event ) { + equal( that, this ); + equal( that.element.find( "strong" )[0], event.currentTarget ); + equal( "keyup", event.type ); + }, + keydown: function(event) { + equal( that, this ); + equal( that.element.find( "strong" )[0], event.currentTarget ); + equal( "keydown", event.type ); + } + }); + // trigger events on both widget and descendent to ensure that only descendent receives them + widget = $( "<div><p><strong>hello</strong> world</p></div>" ) + .testWidget() + .trigger( "keyup" ) + .trigger( "keydown" ); + descendant = widget.find( "strong" ) + .trigger( "keyup" ) + .trigger( "keydown" ); + widget + .testWidget( "disable" ) + .trigger( "keyup" ) + .trigger( "keydown" ); + descendant + .trigger( "keyup" ) + .trigger( "keydown" ); + widget + .testWidget( "enable" ) + .trigger( "keyup" ) + .trigger( "keydown" ); + descendant + .trigger( "keyup" ) + .trigger( "keydown" ); + descendant + .addClass( "ui-state-disabled" ) + .trigger( "keyup" ) + .trigger( "keydown" ); + widget + .testWidget( "destroy" ) + .trigger( "keyup" ) + .trigger( "keydown" ); + descendant + .trigger( "keyup" ) + .trigger( "keydown" ); +}); + +test( "_on() with delegate", function() { + expect( 8 ); + $.widget( "ui.testWidget", { + _create: function() { + var uuid = this.uuid; + this.element = { + bind: function( event, handler ) { + equal( event, "click.testWidget" + uuid ); + ok( $.isFunction(handler) ); + }, + trigger: $.noop + }; + this.widget = function() { + return { + delegate: function( selector, event, handler ) { + equal( selector, "a" ); + equal( event, "click.testWidget" + uuid ); + ok( $.isFunction(handler) ); + } + }; + }; + this._on({ + "click": "handler", + "click a": "handler" + }); + this.widget = function() { + return { + delegate: function( selector, event, handler ) { + equal( selector, "form fieldset > input" ); + equal( event, "change.testWidget" + uuid ); + ok( $.isFunction(handler) ); + } + }; + }; + this._on({ + "change form fieldset > input": "handler" + }); + } + }); + $.ui.testWidget(); +}); + +test( "_on() with delegate to descendent", function() { + expect( 4 ); + $.widget( "ui.testWidget", { + _create: function() { + this.target = $( "<p><strong>hello</strong> world</p>" ); + this.child = this.target.children(); + this._on( this.target, { + "keyup": "handlerDirect", + "keyup strong": "handlerDelegated" + }); + this.child.trigger( "keyup" ); + }, + handlerDirect: function( event ) { + deepEqual( event.currentTarget, this.target[ 0 ] ); + deepEqual( event.target, this.child[ 0 ] ); + }, + handlerDelegated: function( event ) { + deepEqual( event.currentTarget, this.child[ 0 ] ); + deepEqual( event.target, this.child[ 0 ] ); + } + }); + $.ui.testWidget(); +}); + +test( "_on() to common element", function() { + expect( 1 ); + $.widget( "ui.testWidget", { + _create: function() { + this._on( this.document, { + "customevent": "_handler" + }); + }, + _handler: function() { + ok( true, "handler triggered" ); + } + }); + var widget = $( "#widget" ).testWidget().data( "ui-testWidget" ); + $( "#widget-wrapper" ).testWidget(); + widget.destroy(); + $( document ).trigger( "customevent" ); +}); + +test( "_off() - single event", function() { + expect( 3 ); + + $.widget( "ui.testWidget", {} ); + var shouldTriggerWidget, shouldTriggerOther, + element = $( "#widget" ), + widget = element.testWidget().data( "ui-testWidget" ); + widget._on( element, { foo: function() { + ok( shouldTriggerWidget, "foo called from _on" ); + }}); + element.bind( "foo", function() { + ok( shouldTriggerOther, "foo called from bind" ); + }); + shouldTriggerWidget = true; + shouldTriggerOther = true; + element.trigger( "foo" ); + shouldTriggerWidget = false; + widget._off( element, "foo" ); + element.trigger( "foo" ); +}); + +test( "_off() - multiple events", function() { + expect( 6 ); + + $.widget( "ui.testWidget", {} ); + var shouldTriggerWidget, shouldTriggerOther, + element = $( "#widget" ), + widget = element.testWidget().data( "ui-testWidget" ); + widget._on( element, { + foo: function() { + ok( shouldTriggerWidget, "foo called from _on" ); + }, + bar: function() { + ok( shouldTriggerWidget, "bar called from _on" ); + } + }); + element.bind( "foo bar", function( event ) { + ok( shouldTriggerOther, event.type + " called from bind" ); + }); + shouldTriggerWidget = true; + shouldTriggerOther = true; + element.trigger( "foo" ); + element.trigger( "bar" ); + shouldTriggerWidget = false; + widget._off( element, "foo bar" ); + element.trigger( "foo" ); + element.trigger( "bar" ); +}); + +test( "_off() - all events", function() { + expect( 6 ); + + $.widget( "ui.testWidget", {} ); + var shouldTriggerWidget, shouldTriggerOther, + element = $( "#widget" ), + widget = element.testWidget().data( "ui-testWidget" ); + widget._on( element, { + foo: function() { + ok( shouldTriggerWidget, "foo called from _on" ); + }, + bar: function() { + ok( shouldTriggerWidget, "bar called from _on" ); + } + }); + element.bind( "foo bar", function( event ) { + ok( shouldTriggerOther, event.type + " called from bind" ); + }); + shouldTriggerWidget = true; + shouldTriggerOther = true; + element.trigger( "foo" ); + element.trigger( "bar" ); + shouldTriggerWidget = false; + widget._off( element ); + element.trigger( "foo" ); + element.trigger( "bar" ); +}); + +test( "._hoverable()", function() { + expect( 10 ); + $.widget( "ui.testWidget", { + _create: function() { + this._hoverable( this.element.children() ); + } + }); + + var div = $( "#widget" ).testWidget().children(); + ok( !div.hasClass( "ui-state-hover" ), "not hovered on init" ); + div.trigger( "mouseenter" ); + ok( div.hasClass( "ui-state-hover" ), "hovered after mouseenter" ); + div.trigger( "mouseleave" ); + ok( !div.hasClass( "ui-state-hover" ), "not hovered after mouseleave" ); + + div.trigger( "mouseenter" ); + ok( div.hasClass( "ui-state-hover" ), "hovered after mouseenter" ); + $( "#widget" ).testWidget( "disable" ); + ok( !div.hasClass( "ui-state-hover" ), "not hovered while disabled" ); + div.trigger( "mouseenter" ); + ok( !div.hasClass( "ui-state-hover" ), "can't hover while disabled" ); + $( "#widget" ).testWidget( "enable" ); + ok( !div.hasClass( "ui-state-hover" ), "enabling doesn't reset hover" ); + + div.trigger( "mouseenter" ); + ok( div.hasClass( "ui-state-hover" ), "hovered after mouseenter" ); + $( "#widget" ).testWidget( "destroy" ); + ok( !div.hasClass( "ui-state-hover" ), "not hovered after destroy" ); + div.trigger( "mouseenter" ); + ok( !div.hasClass( "ui-state-hover" ), "event handler removed on destroy" ); +}); + +test( "._focusable()", function() { + expect( 10 ); + $.widget( "ui.testWidget", { + _create: function() { + this._focusable( this.element.children() ); + } + }); + + var div = $( "#widget" ).testWidget().children(); + ok( !div.hasClass( "ui-state-focus" ), "not focused on init" ); + div.trigger( "focusin" ); + ok( div.hasClass( "ui-state-focus" ), "focused after explicit focus" ); + div.trigger( "focusout" ); + ok( !div.hasClass( "ui-state-focus" ), "not focused after blur" ); + + div.trigger( "focusin" ); + ok( div.hasClass( "ui-state-focus" ), "focused after explicit focus" ); + $( "#widget" ).testWidget( "disable" ); + ok( !div.hasClass( "ui-state-focus" ), "not focused while disabled" ); + div.trigger( "focusin" ); + ok( !div.hasClass( "ui-state-focus" ), "can't focus while disabled" ); + $( "#widget" ).testWidget( "enable" ); + ok( !div.hasClass( "ui-state-focus" ), "enabling doesn't reset focus" ); + + div.trigger( "focusin" ); + ok( div.hasClass( "ui-state-focus" ), "focused after explicit focus" ); + $( "#widget" ).testWidget( "destroy" ); + ok( !div.hasClass( "ui-state-focus" ), "not focused after destroy" ); + div.trigger( "focusin" ); + ok( !div.hasClass( "ui-state-focus" ), "event handler removed on destroy" ); +}); + +test( "._trigger() - no event, no ui", function() { + expect( 7 ); + var handlers = []; + + $.widget( "ui.testWidget", { + _create: function() {} + }); + + $( "#widget" ).testWidget({ + foo: function( event, ui ) { + deepEqual( event.type, "testwidgetfoo", "correct event type in callback" ); + deepEqual( ui, {}, "empty ui hash passed" ); + handlers.push( "callback" ); + } + }); + $( document ).add( "#widget-wrapper" ).add( "#widget" ) + .bind( "testwidgetfoo", function( event, ui ) { + deepEqual( ui, {}, "empty ui hash passed" ); + handlers.push( this ); + }); + deepEqual( $( "#widget" ).data( "ui-testWidget" )._trigger( "foo" ), true, + "_trigger returns true when event is not cancelled" ); + deepEqual( handlers, [ + $( "#widget" )[ 0 ], + $( "#widget-wrapper" )[ 0 ], + document, + "callback" + ], "event bubbles and then invokes callback" ); + + $( document ).unbind( "testwidgetfoo" ); +}); + +test( "._trigger() - cancelled event", function() { + expect( 3 ); + + $.widget( "ui.testWidget", { + _create: function() {} + }); + + $( "#widget" ).testWidget({ + foo: function() { + ok( true, "callback invoked even if event is cancelled" ); + } + }) + .bind( "testwidgetfoo", function() { + ok( true, "event was triggered" ); + return false; + }); + deepEqual( $( "#widget" ).data( "ui-testWidget" )._trigger( "foo" ), false, + "_trigger returns false when event is cancelled" ); +}); + +test( "._trigger() - cancelled callback", function() { + expect( 1 ); + $.widget( "ui.testWidget", { + _create: function() {} + }); + + $( "#widget" ).testWidget({ + foo: function() { + return false; + } + }); + deepEqual( $( "#widget" ).data( "ui-testWidget" )._trigger( "foo" ), false, + "_trigger returns false when callback returns false" ); +}); + +test( "._trigger() - provide event and ui", function() { + expect( 7 ); + + var originalEvent = $.Event( "originalTest" ); + $.widget( "ui.testWidget", { + _create: function() {}, + testEvent: function() { + var ui = { + foo: "bar", + baz: { + qux: 5, + quux: 20 + } + }; + this._trigger( "foo", originalEvent, ui ); + deepEqual( ui, { + foo: "notbar", + baz: { + qux: 10, + quux: "jQuery" + } + }, "ui object modified" ); + } + }); + $( "#widget" ).bind( "testwidgetfoo", function( event, ui ) { + equal( event.originalEvent, originalEvent, "original event object passed" ); + deepEqual( ui, { + foo: "bar", + baz: { + qux: 5, + quux: 20 + } + }, "ui hash passed" ); + ui.foo = "notbar"; + }); + $( "#widget-wrapper" ).bind( "testwidgetfoo", function( event, ui ) { + equal( event.originalEvent, originalEvent, "original event object passed" ); + deepEqual( ui, { + foo: "notbar", + baz: { + qux: 5, + quux: 20 + } + }, "modified ui hash passed" ); + ui.baz.qux = 10; + }); + $( "#widget" ).testWidget({ + foo: function( event, ui ) { + equal( event.originalEvent, originalEvent, "original event object passed" ); + deepEqual( ui, { + foo: "notbar", + baz: { + qux: 10, + quux: 20 + } + }, "modified ui hash passed" ); + ui.baz.quux = "jQuery"; + } + }) + .testWidget( "testEvent" ); +}); + +test( "._trigger() - array as ui", function() { + // #6795 - Widget: handle array arguments to _trigger consistently + expect( 4 ); + + $.widget( "ui.testWidget", { + _create: function() {}, + testEvent: function() { + var ui = { + foo: "bar", + baz: { + qux: 5, + quux: 20 + } + }, + extra = { + bar: 5 + }; + this._trigger( "foo", null, [ ui, extra ] ); + } + }); + $( "#widget" ).bind( "testwidgetfoo", function( event, ui, extra ) { + deepEqual( ui, { + foo: "bar", + baz: { + qux: 5, + quux: 20 + } + }, "event: ui hash passed" ); + deepEqual( extra, { + bar: 5 + }, "event: extra argument passed" ); + }); + $( "#widget" ).testWidget({ + foo: function( event, ui, extra ) { + deepEqual( ui, { + foo: "bar", + baz: { + qux: 5, + quux: 20 + } + }, "callback: ui hash passed" ); + deepEqual( extra, { + bar: 5 + }, "callback: extra argument passed" ); + } + }) + .testWidget( "testEvent" ); +}); + +test( "._trigger() - instance as element", function() { + expect( 4 ); + $.widget( "ui.testWidget", { + defaultElement: null, + testEvent: function() { + this._trigger( "foo", null, { foo: "bar" } ); + } + }); + var instance = $.ui.testWidget({ + foo: function( event, ui ) { + equal( event.type, "testwidgetfoo", "event object passed to callback" ); + deepEqual( ui, { foo: "bar" }, "ui object passed to callback" ); + } + }); + $( instance ).bind( "testwidgetfoo", function( event, ui ) { + equal( event.type, "testwidgetfoo", "event object passed to event handler" ); + deepEqual( ui, { foo: "bar" }, "ui object passed to event handler" ); + }); + instance.testEvent(); +}); + +(function() { + function shouldDestroy( expected, callback ) { + expect( 1 ); + var destroyed = false; + $.widget( "ui.testWidget", { + _create: function() {}, + destroy: function() { + destroyed = true; + } + }); + callback(); + equal( destroyed, expected ); + } + + test( "auto-destroy - .remove()", function() { + shouldDestroy( true, function() { + $( "#widget" ).testWidget().remove(); + }); + }); + + test( "auto-destroy - .remove() when disabled", function() { + shouldDestroy( true, function() { + $( "#widget" ).testWidget({ disabled: true }).remove(); + }); + }); + + test( "auto-destroy - .remove() on parent", function() { + shouldDestroy( true, function() { + $( "#widget" ).testWidget().parent().remove(); + }); + }); + + test( "auto-destroy - .remove() on child", function() { + shouldDestroy( false, function() { + $( "#widget" ).testWidget().children().remove(); + }); + }); + + test( "auto-destroy - .empty()", function() { + shouldDestroy( false, function() { + $( "#widget" ).testWidget().empty(); + }); + }); + + test( "auto-destroy - .empty() on parent", function() { + shouldDestroy( true, function() { + $( "#widget" ).testWidget().parent().empty(); + }); + }); + + test( "auto-destroy - .detach()", function() { + shouldDestroy( false, function() { + $( "#widget" ).testWidget().detach(); + }); + }); + + test( "destroy - remove event bubbling", function() { + shouldDestroy( false, function() { + $( "<div>child</div>" ).appendTo( $( "#widget" ).testWidget() ) + .trigger( "remove" ); + }); + }); +}()); + +test( "redefine", function() { + expect( 4 ); + $.widget( "ui.testWidget", { + method: function( str ) { + strictEqual( this, instance, "original invoked with correct this" ); + equal( str, "bar", "original invoked with correct parameter" ); + } + }); + $.ui.testWidget.foo = "bar"; + $.widget( "ui.testWidget", $.ui.testWidget, { + method: function( str ) { + equal( str, "foo", "new invoked with correct parameter" ); + this._super( "bar" ); + } + }); + + var instance = new $.ui.testWidget({}); + instance.method( "foo" ); + equal( $.ui.testWidget.foo, "bar", "static properties remain" ); +}); + +test( "redefine deep prototype chain", function() { + expect( 8 ); + $.widget( "ui.testWidget", { + method: function( str ) { + strictEqual( this, instance, "original invoked with correct this" ); + equal( str, "level 4", "original invoked with correct parameter" ); + } + }); + $.widget( "ui.testWidget2", $.ui.testWidget, { + method: function( str ) { + strictEqual( this, instance, "testWidget2 invoked with correct this" ); + equal( str, "level 2", "testWidget2 invoked with correct parameter" ); + this._super( "level 3" ); + } + }); + $.widget( "ui.testWidget3", $.ui.testWidget2, { + method: function( str ) { + strictEqual( this, instance, "testWidget3 invoked with correct this" ); + equal( str, "level 1", "testWidget3 invoked with correct parameter" ); + this._super( "level 2" ); + } + }); + // redefine testWidget after other widgets have inherited from it + // this tests whether the inheriting widgets get updated prototype chains + $.widget( "ui.testWidget", $.ui.testWidget, { + method: function( str ) { + strictEqual( this, instance, "new invoked with correct this" ); + equal( str, "level 3", "new invoked with correct parameter" ); + this._super( "level 4" ); + } + }); + // redefine testWidget3 after it has been automatically redefined + // this tests whether we properly handle _super() when the topmost prototype + // doesn't have the method defined + $.widget( "ui.testWidget3", $.ui.testWidget3, {} ); + + var instance = new $.ui.testWidget3({}); + instance.method( "level 1" ); + + delete $.ui.testWidget3; + delete $.ui.testWidget2; +}); + +test( "redefine - widgetEventPrefix", function() { + expect( 2 ); + + $.widget( "ui.testWidget", { + widgetEventPrefix: "test" + }); + equal( $.ui.testWidget.prototype.widgetEventPrefix, "test", + "cusotm prefix in original" ); + + $.widget( "ui.testWidget", $.ui.testWidget, {} ); + equal( $.ui.testWidget.prototype.widgetEventPrefix, "test", + "cusotm prefix in extension" ); + +}); + +test( "mixins", function() { + expect( 2 ); + + var mixin = { + method: function() { + return "mixed " + this._super(); + } + }; + + $.widget( "ui.testWidget1", { + method: function() { + return "testWidget1"; + } + }); + $.widget( "ui.testWidget2", { + method: function() { + return "testWidget2"; + } + }); + $.widget( "ui.testWidget1", $.ui.testWidget1, mixin ); + $.widget( "ui.testWidget2", $.ui.testWidget2, mixin ); + + equal( $( "<div>" ).testWidget1().testWidget1( "method" ), + "mixed testWidget1", "testWidget1 mixin successful" ); + equal( $( "<div>" ).testWidget2().testWidget2( "method" ), + "mixed testWidget2", "testWidget2 mixin successful" ); +}); + +asyncTest( "_delay", function() { + expect( 6 ); + var order = 0, + that; + $.widget( "ui.testWidget", { + defaultElement: null, + _create: function() { + that = this; + var timer = this._delay(function() { + strictEqual( this, that ); + equal( order, 1 ); + start(); + }, 500); + ok( timer !== undefined ); + timer = this._delay("callback"); + ok( timer !== undefined ); + }, + callback: function() { + strictEqual( this, that ); + equal( order, 0 ); + order += 1; + } + }); + $( "#widget" ).testWidget(); +}); + +test( "$.widget.bridge()", function() { + expect( 9 ); + + var instance, ret, + elem = $( "<div>" ); + + function TestWidget( options, element ) { + deepEqual( options, { foo: "bar" }, "options passed" ); + strictEqual( element, elem[ 0 ], "element passed" ); + } + + $.extend( TestWidget.prototype, { + method: function( param ) { + ok( true, "method called via .pluginName(methodName)" ); + equal( param, "value1", + "parameter passed via .pluginName(methodName, param)" ); + }, + getter: function() { + return "qux"; + } + }); + + $.widget.bridge( "testWidget", TestWidget ); + + ok( $.isFunction( $.fn.testWidget ), "jQuery plugin was created" ); + + strictEqual( elem.testWidget({ foo: "bar" }), elem, "plugin returns original jQuery object" ); + instance = elem.data( "testWidget" ); + equal( typeof instance, "object", "instance stored in .data(pluginName)" ); + + ret = elem.testWidget( "method", "value1" ); + equal( ret, elem, "jQuery object returned from method call" ); + + ret = elem.testWidget( "getter" ); + equal( ret, "qux", "getter returns value" ); +}); + +test( "$.widget.bridge() - widgetFullName", function() { + expect( 1 ); + + var instance, + elem = $( "<div>" ); + + function TestWidget() {} + TestWidget.prototype.widgetFullName = "custom-widget"; + $.widget.bridge( "testWidget", TestWidget ); + + elem.testWidget(); + instance = elem.data( "custom-widget" ); + equal( typeof instance, "object", "instance stored in .data(widgetFullName)" ); +}); + +}( jQuery ) ); diff --git a/apps/it/static/js/ui/tests/unit/widget/widget_extend.js b/apps/it/static/js/ui/tests/unit/widget/widget_extend.js new file mode 100644 index 0000000..14f9a46 --- /dev/null +++ b/apps/it/static/js/ui/tests/unit/widget/widget_extend.js @@ -0,0 +1,105 @@ +test( "$.widget.extend()", function() { + expect( 27 ); + + var ret, empty, optionsWithLength, optionsWithDate, myKlass, customObject, optionsWithCustomObject, nullUndef, + target, recursive, obj, input, output, + settings = { xnumber1: 5, xnumber2: 7, xstring1: "peter", xstring2: "pan" }, + options = { xnumber2: 1, xstring2: "x", xxx: "newstring" }, + optionsCopy = { xnumber2: 1, xstring2: "x", xxx: "newstring" }, + merged = { xnumber1: 5, xnumber2: 1, xstring1: "peter", xstring2: "x", xxx: "newstring" }, + deep1 = { foo: { bar: true } }, + deep2 = { foo: { baz: true }, foo2: document }, + deep2copy = { foo: { baz: true }, foo2: document }, + deepmerged = { foo: { bar: true, baz: true }, foo2: document }, + arr = [1, 2, 3], + nestedarray = { arr: arr }, + defaults = { xnumber1: 5, xnumber2: 7, xstring1: "peter", xstring2: "pan" }, + defaultsCopy = { xnumber1: 5, xnumber2: 7, xstring1: "peter", xstring2: "pan" }, + options1 = { xnumber2: 1, xstring2: "x" }, + options1Copy = { xnumber2: 1, xstring2: "x" }, + options2 = { xstring2: "xx", xxx: "newstringx" }, + options2Copy = { xstring2: "xx", xxx: "newstringx" }, + merged2 = { xnumber1: 5, xnumber2: 1, xstring1: "peter", xstring2: "xx", xxx: "newstringx" }; + + $.widget.extend( settings, options ); + deepEqual( settings, merged, "Check if extended: settings must be extended" ); + deepEqual( options, optionsCopy, "Check if not modified: options must not be modified" ); + + $.widget.extend( deep1, deep2 ); + deepEqual( deep1.foo, deepmerged.foo, "Check if foo: settings must be extended" ); + deepEqual( deep2.foo, deep2copy.foo, "Check if not deep2: options must not be modified" ); + equal( deep1.foo2, document, "Make sure that a deep clone was not attempted on the document" ); + + strictEqual( $.widget.extend({}, nestedarray).arr, arr, "Don't clone arrays" ); + ok( $.isPlainObject( $.widget.extend({ arr: arr }, { arr: {} }).arr ), "Cloned object heve to be an plain object" ); + + empty = {}; + optionsWithLength = { foo: { length: -1 } }; + $.widget.extend( empty, optionsWithLength ); + deepEqual( empty.foo, optionsWithLength.foo, "The length property must copy correctly" ); + + empty = {}; + optionsWithDate = { foo: { date: new Date() } }; + $.widget.extend( empty, optionsWithDate ); + deepEqual( empty.foo, optionsWithDate.foo, "Dates copy correctly" ); + + myKlass = function() {}; + customObject = new myKlass(); + optionsWithCustomObject = { foo: { date: customObject } }; + empty = {}; + $.widget.extend( empty, optionsWithCustomObject ); + strictEqual( empty.foo.date, customObject, "Custom objects copy correctly (no methods)" ); + + // Makes the class a little more realistic + myKlass.prototype = { someMethod: function(){} }; + empty = {}; + $.widget.extend( empty, optionsWithCustomObject ); + strictEqual( empty.foo.date, customObject, "Custom objects copy correctly" ); + + ret = $.widget.extend({ foo: 4 }, { foo: Number(5) } ); + equal( ret.foo, 5, "Wrapped numbers copy correctly" ); + + nullUndef = $.widget.extend( {}, options, { xnumber2: null } ); + strictEqual( nullUndef.xnumber2, null, "Check to make sure null values are copied"); + + nullUndef = $.widget.extend( {}, options, { xnumber2: undefined } ); + strictEqual( nullUndef.xnumber2, options.xnumber2, "Check to make sure undefined values are not copied"); + + nullUndef = $.widget.extend( {}, options, { xnumber0: null } ); + strictEqual( nullUndef.xnumber0, null, "Check to make sure null values are inserted"); + + target = {}; + recursive = { foo:target, bar:5 }; + $.widget.extend( target, recursive ); + deepEqual( target, { foo: {}, bar: 5 }, "Check to make sure a recursive obj doesn't go never-ending loop by not copying it over" ); + + ret = $.widget.extend( { foo: [] }, { foo: [0] } ); // 1907 + equal( ret.foo.length, 1, "Check to make sure a value with coersion 'false' copies over when necessary to fix #1907" ); + + ret = $.widget.extend( { foo: "1,2,3" }, { foo: [ 1, 2, 3 ] } ); + deepEqual( ret.foo, [ 1, 2, 3 ], "Properly extend a string to array." ); + + ret = $.widget.extend( { foo: "1,2,3" }, { foo: { to: "object" } } ); + deepEqual( ret.foo, { to: "object" }, "Properly extend a string to object." ); + + ret = $.widget.extend( { foo: "bar" }, { foo: null } ); + strictEqual( ret.foo, null, "Make sure a null value doesn't crash with deep extend, for #1908" ); + + obj = { foo: null }; + $.widget.extend( obj, { foo:"notnull" } ); + equal( obj.foo, "notnull", "Make sure a null value can be overwritten" ); + + settings = $.widget.extend( {}, defaults, options1, options2 ); + deepEqual( settings, merged2, "Check if extended: settings must be extended" ); + deepEqual( defaults, defaultsCopy, "Check if not modified: options1 must not be modified" ); + deepEqual( options1, options1Copy, "Check if not modified: options1 must not be modified" ); + deepEqual( options2, options2Copy, "Check if not modified: options2 must not be modified" ); + + input = { + key: [ 1, 2, 3 ] + }; + output = $.widget.extend( {}, input ); + deepEqual( input, output, "don't clone arrays" ); + input.key[0] = 10; + deepEqual( input, output, "don't clone arrays" ); +}); |