diff options
Diffstat (limited to 'wqflask/wqflask/static/new/packages/ValidationPlugin/test')
21 files changed, 6754 insertions, 4639 deletions
diff --git a/wqflask/wqflask/static/new/packages/ValidationPlugin/test/aria.js b/wqflask/wqflask/static/new/packages/ValidationPlugin/test/aria.js new file mode 100644 index 00000000..8ac7d1bc --- /dev/null +++ b/wqflask/wqflask/static/new/packages/ValidationPlugin/test/aria.js @@ -0,0 +1,83 @@ +module("aria"); + +test("Invalid field adds aria-invalid=true", function() { + var ariaInvalidFirstName = $("#ariaInvalidFirstName"), + form = $("#ariaInvalid"); + + form.validate({ + rules: { + ariaInvalidFirstName: "required" + } + }); + ariaInvalidFirstName.val(""); + ariaInvalidFirstName.valid(); + equal(ariaInvalidFirstName.attr("aria-invalid"), "true"); +}); + +test("Valid field adds aria-invalid=false", function() { + var ariaInvalidFirstName = $("#ariaInvalidFirstName"), + form = $("#ariaInvalid"); + + form.validate({ + rules: { + ariaInvalidFirstName: "required" + } + }); + ariaInvalidFirstName.val("not empty"); + ariaInvalidFirstName.valid(); + equal(ariaInvalidFirstName.attr("aria-invalid"), "false"); + equal($("#ariaInvalid [aria-invalid=false]").length, 1); +}); + +test("resetForm(): removes all aria-invalid attributes", function() { + var ariaInvalidFirstName = $("#ariaInvalidFirstName"), + form = $("#ariaInvalid"), + validator = form.validate({ + rules: { + ariaInvalidFirstName: "required" + } + }); + + ariaInvalidFirstName.val("not empty"); + ariaInvalidFirstName.valid(); + validator.resetForm(); + equal($("#ariaInvalid [aria-invalid]").length, 0, "resetForm() should remove any aria-invalid attributes"); +}); + +test("Static required field adds aria-required", function() { + var ariaRequiredStatic = $("#ariaRequiredStatic"), + form = $("#ariaRequired"); + + form.validate(); + equal(ariaRequiredStatic.attr("aria-required"), "true"); +}); + +test("Data required field adds aria-required", function() { + var ariaRequiredData = $("#ariaRequiredData"), + form = $("#ariaRequired"); + + form.validate(); + equal(ariaRequiredData.attr("aria-required"), "true"); +}); + +test("Class required field adds aria-required", function() { + var ariaRequiredClass = $("#ariaRequiredClass"), + form = $("#ariaRequired"); + + form.validate(); + equal(ariaRequiredClass.attr("aria-required"), "true"); +}); + +test("Dynamically required field adds aria-required after valid()", function() { + var ariaRequiredDynamic = $("#ariaRequiredDynamic"), + form = $("#ariaRequired"); + + form.resetForm(); + form.validate({ + rules: { + ariaRequiredDynamic: "required" + } + }); + ariaRequiredDynamic.valid(); + equal(ariaRequiredDynamic.attr("aria-required"), "true"); +}); diff --git a/wqflask/wqflask/static/new/packages/ValidationPlugin/test/error-placement.js b/wqflask/wqflask/static/new/packages/ValidationPlugin/test/error-placement.js new file mode 100644 index 00000000..40d7e092 --- /dev/null +++ b/wqflask/wqflask/static/new/packages/ValidationPlugin/test/error-placement.js @@ -0,0 +1,358 @@ +module( "placement" ); + +test( "elements() order", function() { + var container = $( "#orderContainer" ), + v = $( "#elementsOrder" ).validate({ + errorLabelContainer: container, + wrap: "li" + }); + + deepEqual( + v.elements().map( function() { + return $( this ).attr( "id" ); + }).get(), + [ + "order1", + "order2", + "order3", + "order4", + "order5", + "order6" + ], + "elements must be in document order" + ); + + v.form(); + deepEqual( + container.children().map( function() { + return $( this ).attr( "id" ); + }).get(), + [ + "order1-error", + "order2-error", + "order3-error", + "order4-error", + "order5-error", + "order6-error" + ], + "labels in error container must be in document order" + ); +}); + +test( "error containers, simple", function() { + expect( 14 ); + var container = $( "#simplecontainer" ), + v = $( "#form" ).validate({ + errorLabelContainer: container, + showErrors: function() { + container.find( "h3" ).html( jQuery.validator.format( "There are {0} errors in your form.", this.size()) ); + this.defaultShowErrors(); + } + }); + + v.prepareForm(); + ok( v.valid(), "form is valid" ); + equal( 0, container.find( ".error:not(input)" ).length, "There should be no error labels" ); + equal( "", container.find( "h3" ).html() ); + + v.prepareForm(); + v.errorList = [ + { + message: "bar", + element: { + name: "foo" + } + }, + { + message: "necessary", + element: { + name: "required" + } + } + ]; + + ok( !v.valid(), "form is not valid after adding errors manually" ); + v.showErrors(); + equal( container.find( ".error:not(input)" ).length, 2, "There should be two error labels" ); + ok( container.is( ":visible" ), "Check that the container is visible" ); + container.find( ".error:not(input)" ).each(function() { + ok( $( this ).is( ":visible" ), "Check that each label is visible" ); + }); + equal( "There are 2 errors in your form.", container.find( "h3" ).html() ); + + v.prepareForm(); + ok( v.valid(), "form is valid after a reset" ); + v.showErrors(); + equal( container.find( ".error:not(input)" ).length, 2, "There should still be two error labels" ); + ok( container.is( ":hidden" ), "Check that the container is hidden" ); + container.find( ".error:not(input)" ).each(function() { + ok( $( this ).is( ":hidden" ), "Check that each label is hidden" ); + }); +}); + +test( "error containers, with labelcontainer I", function() { + expect( 16 ); + var container = $( "#container" ), + labelcontainer = $( "#labelcontainer" ), + v = $( "#form" ).validate({ + errorContainer: container, + errorLabelContainer: labelcontainer, + wrapper: "li" + }); + + ok( v.valid(), "form is valid" ); + equal( 0, container.find( ".error:not(input)" ).length, "There should be no error labels in the container" ); + equal( 0, labelcontainer.find( ".error:not(input)" ).length, "There should be no error labels in the labelcontainer" ); + equal( 0, labelcontainer.find( "li" ).length, "There should be no lis labels in the labelcontainer" ); + + v.errorList = [ + { + message: "bar", + element: { + name: "foo" + } + }, + { + name: "required", + message: "necessary", + element: { + name: "required" + } + } + ]; + + ok( !v.valid(), "form is not valid after adding errors manually" ); + v.showErrors(); + equal( 0, container.find( ".error:not(input)" ).length, "There should be no error label in the container" ); + equal( 2, labelcontainer.find( ".error:not(input)" ).length, "There should be two error labels in the labelcontainer" ); + equal( 2, labelcontainer.find( "li" ).length, "There should be two error lis in the labelcontainer" ); + ok( container.is( ":visible" ), "Check that the container is visible" ); + ok( labelcontainer.is( ":visible" ), "Check that the labelcontainer is visible" ); + labelcontainer.find( ".error:not(input)" ).each(function() { + ok( $( this ).is( ":visible" ), "Check that each label is visible1" ); + equal( "li", $( this ).parent()[0].tagName.toLowerCase(), "Check that each label is wrapped in an li" ); + ok( $( this ).parent( "li" ).is( ":visible" ), "Check that each parent li is visible" ); + }); +}); + +test( "errorcontainer, show/hide only on submit", function() { + expect( 14 ); + var container = $( "#container" ), + labelContainer = $( "#labelcontainer" ), + v = $( "#testForm1" ).bind( "invalid-form.validate", function() { + ok( true, "invalid-form event triggered called" ); + }).validate({ + errorContainer: container, + errorLabelContainer: labelContainer, + showErrors: function() { + container.html( jQuery.validator.format( "There are {0} errors in your form.", this.numberOfInvalids()) ); + ok( true, "showErrors called" ); + this.defaultShowErrors(); + } + }); + + equal( "", container.html(), "must be empty" ); + equal( "", labelContainer.html(), "must be empty" ); + // validate whole form, both showErrors and invalidHandler must be called once + // preferably invalidHandler first, showErrors second + ok( !v.form(), "invalid form" ); + equal( 2, labelContainer.find( ".error:not(input)" ).length ); + equal( "There are 2 errors in your form.", container.html() ); + ok( labelContainer.is( ":visible" ), "must be visible" ); + ok( container.is( ":visible" ), "must be visible" ); + + $( "#firstname" ).val( "hix" ).keyup(); + $( "#testForm1" ).triggerHandler( "keyup", [ + jQuery.event.fix({ + type: "keyup", + target: $( "#firstname" )[ 0 ] + }) + ]); + equal( 1, labelContainer.find( ".error:visible" ).length ); + equal( "There are 1 errors in your form.", container.html() ); + + $( "#lastname" ).val( "abc" ); + ok( v.form(), "Form now valid, trigger showErrors but not invalid-form" ); +}); + +test( "test label used as error container", function(assert) { + expect( 8 ); + var form = $( "#testForm16" ), + field = $( "#testForm16text" ); + + form.validate({ + errorPlacement: function( error, element ) { + // Append error within linked label + $( "label[for='" + element.attr( "id" ) + "']" ).append( error ); + }, + errorElement: "span" + }); + + ok( !field.valid() ); + equal( "Field Label", field.next( "label" ).contents().first().text(), "container label isn't disrupted" ); + assert.hasError(field, "missing"); + ok( !field.attr( "aria-describedby" ), "field does not require aria-describedby attribute" ); + + field.val( "foo" ); + ok( field.valid() ); + equal( "Field Label", field.next( "label" ).contents().first().text(), "container label isn't disrupted" ); + ok( !field.attr( "aria-describedby" ), "field does not require aria-describedby attribute" ); + assert.noErrorFor(field); +}); + +test( "test error placed adjacent to descriptive label", function(assert) { + expect( 8 ); + var form = $( "#testForm16" ), + field = $( "#testForm16text" ); + + form.validate({ + errorElement: "span" + }); + + ok( !field.valid() ); + equal( 1, form.find( "label" ).length ); + equal( "Field Label", form.find( "label" ).text(), "container label isn't disrupted" ); + assert.hasError( field, "missing" ); + + field.val( "foo" ); + ok( field.valid() ); + equal( 1, form.find( "label" ).length ); + equal( "Field Label", form.find( "label" ).text(), "container label isn't disrupted" ); + assert.noErrorFor( field ); +}); + +test( "test descriptive label used alongside error label", function(assert) { + expect( 8 ); + var form = $( "#testForm16" ), + field = $( "#testForm16text" ); + + form.validate({ + errorElement: "label" + }); + + ok( !field.valid() ); + equal( 1, form.find( "label.title" ).length ); + equal( "Field Label", form.find( "label.title" ).text(), "container label isn't disrupted" ); + assert.hasError( field, "missing" ); + + field.val( "foo" ); + ok( field.valid() ); + equal( 1, form.find( "label.title" ).length ); + equal( "Field Label", form.find( "label.title" ).text(), "container label isn't disrupted" ); + assert.noErrorFor( field ); +}); + +test( "test custom errorElement", function(assert) { + expect( 4 ); + var form = $( "#userForm" ), + field = $( "#username" ); + + form.validate({ + messages: { + username: "missing" + }, + errorElement: "label" + }); + + ok( !field.valid() ); + assert.hasError( field, "missing", "Field should have error 'missing'" ); + field.val( "foo" ); + ok( field.valid() ); + assert.noErrorFor( field, "Field should not have a visible error" ); +}); + +test( "test existing label used as error element", function(assert) { + expect( 4 ); + var form = $( "#testForm14" ), + field = $( "#testForm14text" ); + + form.validate({ errorElement: "label" }); + + ok( !field.valid() ); + assert.hasError( field, "required" ); + + field.val( "foo" ); + ok( field.valid() ); + assert.noErrorFor( field ); +}); + +test( "test existing non-label used as error element", function(assert) { + expect( 4 ); + var form = $( "#testForm15" ), + field = $( "#testForm15text" ); + + form.validate({ errorElement: "span" }); + + ok( !field.valid() ); + assert.hasError( field, "required" ); + + field.val( "foo" ); + ok( field.valid() ); + assert.noErrorFor( field ); +}); + +test( "test existing non-error aria-describedby", function( assert ) { + expect( 8 ); + var form = $( "#testForm17" ), + field = $( "#testForm17text" ); + + equal( field.attr( "aria-describedby" ), "testForm17text-description" ); + form.validate({ errorElement: "span" }); + + ok( !field.valid() ); + equal( field.attr( "aria-describedby" ), "testForm17text-description testForm17text-error" ); + assert.hasError( field, "required" ); + + field.val( "foo" ); + ok( field.valid() ); + assert.noErrorFor( field ); + + strictEqual( "This is where you enter your data", $("#testForm17text-description").text() ); + strictEqual( "", $("#testForm17text-error").text(), "Error label is empty for valid field" ); +}); + +test( "test pre-assigned non-error aria-describedby", function( assert ) { + expect( 7 ); + var form = $( "#testForm17" ), + field = $( "#testForm17text" ); + + // Pre-assign error identifier + field.attr( "aria-describedby", "testForm17text-description testForm17text-error" ); + form.validate({ errorElement: "span" }); + + ok( !field.valid() ); + equal( field.attr( "aria-describedby" ), "testForm17text-description testForm17text-error" ); + assert.hasError( field, "required" ); + + field.val( "foo" ); + ok( field.valid() ); + assert.noErrorFor( field ); + + strictEqual( "This is where you enter your data", $("#testForm17text-description").text() ); + strictEqual( "", $("#testForm17text-error").text(), "Error label is empty for valid field" ); +}); + +test( "test id/name containing brackets", function( assert ) { + var form = $( "#testForm18" ), + field = $( "#testForm18\\[text\\]" ); + + form.validate({ + errorElement: "span" + }); + + form.valid(); + field.valid(); + assert.hasError( field, "required" ); +}); + +test( "test id/name containing $", function( assert ) { + var form = $( "#testForm19" ), + field = $( "#testForm19\\$text" ); + + form.validate({ + errorElement: "span" + }); + + field.valid(); + assert.hasError( field, "required" ); +}); diff --git a/wqflask/wqflask/static/new/packages/ValidationPlugin/test/events.html b/wqflask/wqflask/static/new/packages/ValidationPlugin/test/events.html deleted file mode 100755 index eba81b00..00000000 --- a/wqflask/wqflask/static/new/packages/ValidationPlugin/test/events.html +++ /dev/null @@ -1,71 +0,0 @@ -<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> -<html xmlns="http://www.w3.org/1999/xhtml"> -<head> -<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1" /> -<title>Test for jQuery validate() plugin</title> - -<link rel="stylesheet" type="text/css" media="screen" href="css/screen.css" /> -<script src="../lib/jquery.js" type="text/javascript"></script> -<script src="firebug/firebug.js" type="text/javascript"></script> - -<script type="text/javascript"> -$().ready(function() { - var handler = { - focusin: function() { - $(this).addClass("focus"); - }, - focusout: function() { - $(this).removeClass("focus"); - } - } - $("#commentForm").delegate("focusin focusout", ":text, textarea", function(event) { - /* - this.addClass("focus").one("blur", function() { - $(this).removeClass("focus"); - }); - */ - handler[event.type].call(this, arguments); - }); - $("#remove").click(function() { - $("#commentForm").unbind("focusin"); - }) -}); -</script> - -<style type="text/css"> -#commentForm { width: 500px; } -#commentForm label { width: 250px; display: block; float: left; } -#commentForm label.error, #commentForm input.submit { margin-left: 253px; } -.focus { background-color: red; } -</style> - -</head> -<body> -<form class="cmxform" id="commentForm" method="get" action=""> - <fieldset> - <legend>A simple comment form with submit validation and default messages</legend> - <p> - <label for="cname">Name (required, at least 2 characters)</label> - <input id="cname" name="name" class="some other styles {required:true,minLength:2}" /> - <p> - <label for="cemail">E-Mail (required)</label> - <input id="cemail" name="email" class="{required:true,email:true}" /> - </p> - <p> - <label for="curl">URL (optional)</label> - <input id="curl" name="url" class="{url:true}" value="" /> - </p> - <p> - <label for="ccomment">Your comment (required)</label> - <textarea id="ccomment" name="comment" class="{required:true}"></textarea> - </p> - <p> - <input class="submit" type="submit" value="Submit"/> - </p> - </fieldset> -</form> - -<button id="remove">Remove focus handler</button> - -</body> -</html> diff --git a/wqflask/wqflask/static/new/packages/ValidationPlugin/test/firebug/errorIcon.png b/wqflask/wqflask/static/new/packages/ValidationPlugin/test/firebug/errorIcon.png Binary files differdeleted file mode 100755 index 2d75261b..00000000 --- a/wqflask/wqflask/static/new/packages/ValidationPlugin/test/firebug/errorIcon.png +++ /dev/null diff --git a/wqflask/wqflask/static/new/packages/ValidationPlugin/test/firebug/firebug.css b/wqflask/wqflask/static/new/packages/ValidationPlugin/test/firebug/firebug.css deleted file mode 100755 index 1f041c4d..00000000 --- a/wqflask/wqflask/static/new/packages/ValidationPlugin/test/firebug/firebug.css +++ /dev/null @@ -1,209 +0,0 @@ - -html, body { - margin: 0; - background: #FFFFFF; - font-family: Lucida Grande, Tahoma, sans-serif; - font-size: 11px; - overflow: hidden; -} - -a { - text-decoration: none; -} - -a:hover { - text-decoration: underline; -} - -.toolbar { - height: 14px; - border-top: 1px solid ThreeDHighlight; - border-bottom: 1px solid ThreeDShadow; - padding: 2px 6px; - background: ThreeDFace; -} - -.toolbarRight { - position: absolute; - top: 4px; - right: 6px; -} - -#log { - overflow: auto; - position: absolute; - left: 0; - width: 100%; -} - -#commandLine { - position: absolute; - bottom: 0; - left: 0; - width: 100%; - height: 18px; - border: none; - border-top: 1px solid ThreeDShadow; -} - -/************************************************************************************************/ - -.logRow { - position: relative; - border-bottom: 1px solid #D7D7D7; - padding: 2px 4px 1px 6px; - background-color: #FFFFFF; -} - -.logRow-command { - font-family: Monaco, monospace; - color: blue; -} - -.objectBox-null { - padding: 0 2px; - border: 1px solid #666666; - background-color: #888888; - color: #FFFFFF; -} - -.objectBox-string { - font-family: Monaco, monospace; - color: red; - white-space: pre; -} - -.objectBox-number { - color: #000088; -} - -.objectBox-function { - font-family: Monaco, monospace; - color: DarkGreen; -} - -.objectBox-object { - color: DarkGreen; - font-weight: bold; -} - -/************************************************************************************************/ - -.logRow-info, -.logRow-error, -.logRow-warning { - background: #FFFFFF no-repeat 2px 2px; - padding-left: 20px; - padding-bottom: 3px; -} - -.logRow-info { - background-image: url(infoIcon.png); -} - -.logRow-warning { - background-color: cyan; - background-image: url(warningIcon.png); -} - -.logRow-error { - background-color: LightYellow; - background-image: url(errorIcon.png); -} - -.errorMessage { - vertical-align: top; - color: #FF0000; -} - -.objectBox-sourceLink { - position: absolute; - right: 4px; - top: 2px; - padding-left: 8px; - font-family: Lucida Grande, sans-serif; - font-weight: bold; - color: #0000FF; -} - -/************************************************************************************************/ - -.logRow-group { - background: #EEEEEE; - border-bottom: none; -} - -.logGroup { - background: #EEEEEE; -} - -.logGroupBox { - margin-left: 24px; - border-top: 1px solid #D7D7D7; - border-left: 1px solid #D7D7D7; -} - -/************************************************************************************************/ - -.selectorTag, -.selectorId, -.selectorClass { - font-family: Monaco, monospace; - font-weight: normal; -} - -.selectorTag { - color: #0000FF; -} - -.selectorId { - color: DarkBlue; -} - -.selectorClass { - color: red; -} - -/************************************************************************************************/ - -.objectBox-element { - font-family: Monaco, monospace; - color: #000088; -} - -.nodeChildren { - margin-left: 16px; -} - -.nodeTag { - color: blue; -} - -.nodeValue { - color: #FF0000; - font-weight: normal; -} - -.nodeText, -.nodeComment { - margin: 0 2px; - vertical-align: top; -} - -.nodeText { - color: #333333; -} - -.nodeComment { - color: DarkGreen; -} - -/************************************************************************************************/ - -.propertyNameCell { - vertical-align: top; -} - -.propertyName { - font-weight: bold; -} diff --git a/wqflask/wqflask/static/new/packages/ValidationPlugin/test/firebug/firebug.html b/wqflask/wqflask/static/new/packages/ValidationPlugin/test/firebug/firebug.html deleted file mode 100755 index 861e6393..00000000 --- a/wqflask/wqflask/static/new/packages/ValidationPlugin/test/firebug/firebug.html +++ /dev/null @@ -1,23 +0,0 @@ -<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" - "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> - -<html xmlns="http://www.w3.org/1999/xhtml"> - -<head> - <title>Firebug</title> - <link rel="stylesheet" type="text/css" href="firebug.css"> -</head> - -<body> - <div id="toolbar" class="toolbar"> - <a href="#" onclick="parent.console.clear()">Clear</a> - <span class="toolbarRight"> - <a href="#" onclick="parent.console.close()">Close</a> - </span> - </div> - <div id="log"></div> - <input type="text" id="commandLine"> - - <script>parent.onFirebugReady(document);</script> -</body> -</html> diff --git a/wqflask/wqflask/static/new/packages/ValidationPlugin/test/firebug/firebug.js b/wqflask/wqflask/static/new/packages/ValidationPlugin/test/firebug/firebug.js deleted file mode 100755 index eb853b82..00000000 --- a/wqflask/wqflask/static/new/packages/ValidationPlugin/test/firebug/firebug.js +++ /dev/null @@ -1,672 +0,0 @@ - -if (!("console" in window) || !("firebug" in console)) { -(function() -{ - window.console = - { - log: function() - { - logFormatted(arguments, ""); - }, - - debug: function() - { - logFormatted(arguments, "debug"); - }, - - info: function() - { - logFormatted(arguments, "info"); - }, - - warn: function() - { - logFormatted(arguments, "warning"); - }, - - error: function() - { - logFormatted(arguments, "error"); - }, - - assert: function(truth, message) - { - if (!truth) - { - var args = []; - for (var i = 1; i < arguments.length; ++i) - args.push(arguments[i]); - - logFormatted(args.length ? args : ["Assertion Failure"], "error"); - throw message ? message : "Assertion Failure"; - } - }, - - dir: function(object) - { - var html = []; - - var pairs = []; - for (var name in object) - { - try - { - pairs.push([name, object[name]]); - } - catch (exc) - { - } - } - - pairs.sort(function(a, b) { return a[0] < b[0] ? -1 : 1; }); - - html.push('<table>'); - for (var i = 0; i < pairs.length; ++i) - { - var name = pairs[i][0], value = pairs[i][1]; - - html.push('<tr>', - '<td class="propertyNameCell"><span class="propertyName">', - escapeHTML(name), '</span></td>', '<td><span class="propertyValue">'); - appendObject(value, html); - html.push('</span></td></tr>'); - } - html.push('</table>'); - - logRow(html, "dir"); - }, - - dirxml: function(node) - { - var html = []; - - appendNode(node, html); - logRow(html, "dirxml"); - }, - - group: function() - { - logRow(arguments, "group", pushGroup); - }, - - groupEnd: function() - { - logRow(arguments, "", popGroup); - }, - - time: function(name) - { - timeMap[name] = (new Date()).getTime(); - }, - - timeEnd: function(name) - { - if (name in timeMap) - { - var delta = (new Date()).getTime() - timeMap[name]; - logFormatted([name+ ":", delta+"ms"]); - delete timeMap[name]; - } - }, - - count: function() - { - this.warn(["count() not supported."]); - }, - - trace: function() - { - this.warn(["trace() not supported."]); - }, - - profile: function() - { - this.warn(["profile() not supported."]); - }, - - profileEnd: function() - { - }, - - clear: function() - { - consoleBody.innerHTML = ""; - }, - - open: function() - { - toggleConsole(true); - }, - - close: function() - { - if (frameVisible) - toggleConsole(); - } - }; - - // ******************************************************************************************** - - var consoleFrame = null; - var consoleBody = null; - var commandLine = null; - - var frameVisible = false; - var messageQueue = []; - var groupStack = []; - var timeMap = {}; - - var clPrefix = ">>> "; - - var isFirefox = navigator.userAgent.indexOf("Firefox") != -1; - var isIE = navigator.userAgent.indexOf("MSIE") != -1; - var isOpera = navigator.userAgent.indexOf("Opera") != -1; - var isSafari = navigator.userAgent.indexOf("AppleWebKit") != -1; - - // ******************************************************************************************** - - function toggleConsole(forceOpen) - { - frameVisible = forceOpen || !frameVisible; - if (consoleFrame) - consoleFrame.style.visibility = frameVisible ? "visible" : "hidden"; - else - waitForBody(); - } - - function focusCommandLine() - { - toggleConsole(true); - if (commandLine) - commandLine.focus(); - } - - function waitForBody() - { - if (document.body) - createFrame(); - else - setTimeout(waitForBody, 200); - } - - function createFrame() - { - if (consoleFrame) - return; - - window.onFirebugReady = function(doc) - { - window.onFirebugReady = null; - - var toolbar = doc.getElementById("toolbar"); - toolbar.onmousedown = onSplitterMouseDown; - - commandLine = doc.getElementById("commandLine"); - addEvent(commandLine, "keydown", onCommandLineKeyDown); - - addEvent(doc, isIE || isSafari ? "keydown" : "keypress", onKeyDown); - - consoleBody = doc.getElementById("log"); - layout(); - flush(); - } - - var baseURL = getFirebugURL(); - - consoleFrame = document.createElement("iframe"); - consoleFrame.setAttribute("src", baseURL+"/firebug.html"); - consoleFrame.setAttribute("frameBorder", "0"); - consoleFrame.style.visibility = (frameVisible ? "visible" : "hidden"); - consoleFrame.style.zIndex = "2147483647"; - consoleFrame.style.position = "fixed"; - consoleFrame.style.width = "100%"; - consoleFrame.style.left = "0"; - consoleFrame.style.bottom = "0"; - consoleFrame.style.height = "200px"; - document.body.appendChild(consoleFrame); - } - - function getFirebugURL() - { - var scripts = document.getElementsByTagName("script"); - for (var i = 0; i < scripts.length; ++i) - { - if (scripts[i].src.indexOf("firebug.js") != -1) - { - var lastSlash = scripts[i].src.lastIndexOf("/"); - return scripts[i].src.substr(0, lastSlash); - } - } - } - - function evalCommandLine() - { - var text = commandLine.value; - commandLine.value = ""; - - logRow([clPrefix, text], "command"); - - var value; - try - { - value = eval(text); - } - catch (exc) - { - } - - console.log(value); - } - - function layout() - { - var toolbar = consoleBody.ownerDocument.getElementById("toolbar"); - var height = consoleFrame.offsetHeight - (toolbar.offsetHeight + commandLine.offsetHeight); - consoleBody.style.top = toolbar.offsetHeight + "px"; - consoleBody.style.height = height + "px"; - - commandLine.style.top = (consoleFrame.offsetHeight - commandLine.offsetHeight) + "px"; - } - - function logRow(message, className, handler) - { - if (consoleBody) - writeMessage(message, className, handler); - else - { - messageQueue.push([message, className, handler]); - waitForBody(); - } - } - - function flush() - { - var queue = messageQueue; - messageQueue = []; - - for (var i = 0; i < queue.length; ++i) - writeMessage(queue[i][0], queue[i][1], queue[i][2]); - } - - function writeMessage(message, className, handler) - { - var isScrolledToBottom = - consoleBody.scrollTop + consoleBody.offsetHeight >= consoleBody.scrollHeight; - - if (!handler) - handler = writeRow; - - handler(message, className); - - if (isScrolledToBottom) - consoleBody.scrollTop = consoleBody.scrollHeight - consoleBody.offsetHeight; - } - - function appendRow(row) - { - var container = groupStack.length ? groupStack[groupStack.length-1] : consoleBody; - container.appendChild(row); - } - - function writeRow(message, className) - { - var row = consoleBody.ownerDocument.createElement("div"); - row.className = "logRow" + (className ? " logRow-"+className : ""); - row.innerHTML = message.join(""); - appendRow(row); - } - - function pushGroup(message, className) - { - logFormatted(message, className); - - var groupRow = consoleBody.ownerDocument.createElement("div"); - groupRow.className = "logGroup"; - var groupRowBox = consoleBody.ownerDocument.createElement("div"); - groupRowBox.className = "logGroupBox"; - groupRow.appendChild(groupRowBox); - appendRow(groupRowBox); - groupStack.push(groupRowBox); - } - - function popGroup() - { - groupStack.pop(); - } - - // ******************************************************************************************** - - function logFormatted(objects, className) - { - var html = []; - - var format = objects[0]; - var objIndex = 0; - - if (typeof(format) != "string") - { - format = ""; - objIndex = -1; - } - - var parts = parseFormat(format); - for (var i = 0; i < parts.length; ++i) - { - var part = parts[i]; - if (part && typeof(part) == "object") - { - var object = objects[++objIndex]; - part.appender(object, html); - } - else - appendText(part, html); - } - - for (var i = objIndex+1; i < objects.length; ++i) - { - appendText(" ", html); - - var object = objects[i]; - if (typeof(object) == "string") - appendText(object, html); - else - appendObject(object, html); - } - - logRow(html, className); - } - - function parseFormat(format) - { - var parts = []; - - var reg = /((^%|[^\\]%)(\d+)?(\.)([a-zA-Z]))|((^%|[^\\]%)([a-zA-Z]))/; - var appenderMap = {s: appendText, d: appendInteger, i: appendInteger, f: appendFloat}; - - for (var m = reg.exec(format); m; m = reg.exec(format)) - { - var type = m[8] ? m[8] : m[5]; - var appender = type in appenderMap ? appenderMap[type] : appendObject; - var precision = m[3] ? parseInt(m[3]) : (m[4] == "." ? -1 : 0); - - parts.push(format.substr(0, m[0][0] == "%" ? m.index : m.index+1)); - parts.push({appender: appender, precision: precision}); - - format = format.substr(m.index+m[0].length); - } - - parts.push(format); - - return parts; - } - - function escapeHTML(value) - { - function replaceChars(ch) - { - switch (ch) - { - case "<": - return "<"; - case ">": - return ">"; - case "&": - return "&"; - case "'": - return "'"; - case '"': - return """; - } - return "?"; - }; - return String(value).replace(/[<>&"']/g, replaceChars); - } - - function objectToString(object) - { - try - { - return object+""; - } - catch (exc) - { - return null; - } - } - - // ******************************************************************************************** - - function appendText(object, html) - { - html.push(escapeHTML(objectToString(object))); - } - - function appendNull(object, html) - { - html.push('<span class="objectBox-null">', escapeHTML(objectToString(object)), '</span>'); - } - - function appendString(object, html) - { - html.push('<span class="objectBox-string">"', escapeHTML(objectToString(object)), - '"</span>'); - } - - function appendInteger(object, html) - { - html.push('<span class="objectBox-number">', escapeHTML(objectToString(object)), '</span>'); - } - - function appendFloat(object, html) - { - html.push('<span class="objectBox-number">', escapeHTML(objectToString(object)), '</span>'); - } - - function appendFunction(object, html) - { - var reName = /function ?(.*?)\(/; - var m = reName.exec(objectToString(object)); - var name = m ? m[1] : "function"; - html.push('<span class="objectBox-function">', escapeHTML(name), '()</span>'); - } - - function appendObject(object, html) - { - try - { - if (object == undefined) - appendNull("undefined", html); - else if (object == null) - appendNull("null", html); - else if (typeof object == "string") - appendString(object, html); - else if (typeof object == "number") - appendInteger(object, html); - else if (typeof object == "function") - appendFunction(object, html); - else if (object.nodeType == 1) - appendSelector(object, html); - else if (typeof object == "object") - appendObjectFormatted(object, html); - else - appendText(object, html); - } - catch (exc) - { - } - } - - function appendObjectFormatted(object, html) - { - var text = objectToString(object); - var reObject = /\[object (.*?)\]/; - - var m = reObject.exec(text); - html.push('<span class="objectBox-object">', m ? m[1] : text, '</span>') - } - - function appendSelector(object, html) - { - html.push('<span class="objectBox-selector">'); - - html.push('<span class="selectorTag">', escapeHTML(object.nodeName.toLowerCase()), '</span>'); - if (object.id) - html.push('<span class="selectorId">#', escapeHTML(object.id), '</span>'); - if (object.className) - html.push('<span class="selectorClass">.', escapeHTML(object.className), '</span>'); - - html.push('</span>'); - } - - function appendNode(node, html) - { - if (node.nodeType == 1) - { - html.push( - '<div class="objectBox-element">', - '<<span class="nodeTag">', node.nodeName.toLowerCase(), '</span>'); - - for (var i = 0; i < node.attributes.length; ++i) - { - var attr = node.attributes[i]; - if (!attr.specified) - continue; - - html.push(' <span class="nodeName">', attr.nodeName.toLowerCase(), - '</span>="<span class="nodeValue">', escapeHTML(attr.nodeValue), - '</span>"') - } - - if (node.firstChild) - { - html.push('></div><div class="nodeChildren">'); - - for (var child = node.firstChild; child; child = child.nextSibling) - appendNode(child, html); - - html.push('</div><div class="objectBox-element"></<span class="nodeTag">', - node.nodeName.toLowerCase(), '></span></div>'); - } - else - html.push('/></div>'); - } - else if (node.nodeType == 3) - { - html.push('<div class="nodeText">', escapeHTML(node.nodeValue), - '</div>'); - } - } - - // ******************************************************************************************** - - function addEvent(object, name, handler) - { - if (document.all) - object.attachEvent("on"+name, handler); - else - object.addEventListener(name, handler, false); - } - - function removeEvent(object, name, handler) - { - if (document.all) - object.detachEvent("on"+name, handler); - else - object.removeEventListener(name, handler, false); - } - - function cancelEvent(event) - { - if (document.all) - event.cancelBubble = true; - else - event.stopPropagation(); - } - - function onError(msg, href, lineNo) - { - var html = []; - - var lastSlash = href.lastIndexOf("/"); - var fileName = lastSlash == -1 ? href : href.substr(lastSlash+1); - - html.push( - '<span class="errorMessage">', msg, '</span>', - '<div class="objectBox-sourceLink">', fileName, ' (line ', lineNo, ')</div>' - ); - - logRow(html, "error"); - }; - - function onKeyDown(event) - { - if (event.keyCode == 123) - toggleConsole(); - else if ((event.keyCode == 108 || event.keyCode == 76) && event.shiftKey - && (event.metaKey || event.ctrlKey)) - focusCommandLine(); - else - return; - - cancelEvent(event); - } - - function onSplitterMouseDown(event) - { - if (isSafari || isOpera) - return; - - addEvent(document, "mousemove", onSplitterMouseMove); - addEvent(document, "mouseup", onSplitterMouseUp); - - for (var i = 0; i < frames.length; ++i) - { - addEvent(frames[i].document, "mousemove", onSplitterMouseMove); - addEvent(frames[i].document, "mouseup", onSplitterMouseUp); - } - } - - function onSplitterMouseMove(event) - { - var win = document.all - ? event.srcElement.ownerDocument.parentWindow - : event.target.ownerDocument.defaultView; - - var clientY = event.clientY; - if (win != win.parent) - clientY += win.frameElement ? win.frameElement.offsetTop : 0; - - var height = consoleFrame.offsetTop + consoleFrame.clientHeight; - var y = height - clientY; - - consoleFrame.style.height = y + "px"; - layout(); - } - - function onSplitterMouseUp(event) - { - removeEvent(document, "mousemove", onSplitterMouseMove); - removeEvent(document, "mouseup", onSplitterMouseUp); - - for (var i = 0; i < frames.length; ++i) - { - removeEvent(frames[i].document, "mousemove", onSplitterMouseMove); - removeEvent(frames[i].document, "mouseup", onSplitterMouseUp); - } - } - - function onCommandLineKeyDown(event) - { - if (event.keyCode == 13) - evalCommandLine(); - else if (event.keyCode == 27) - commandLine.value = ""; - } - - window.onerror = onError; - addEvent(document, isIE || isSafari ? "keydown" : "keypress", onKeyDown); - - if (document.documentElement.getAttribute("debug") == "true") - toggleConsole(true); -})(); -} diff --git a/wqflask/wqflask/static/new/packages/ValidationPlugin/test/firebug/firebugx.js b/wqflask/wqflask/static/new/packages/ValidationPlugin/test/firebug/firebugx.js deleted file mode 100755 index 5a467fc1..00000000 --- a/wqflask/wqflask/static/new/packages/ValidationPlugin/test/firebug/firebugx.js +++ /dev/null @@ -1,10 +0,0 @@ - -if (!("console" in window) || !("firebug" in console)) -{ - var names = ["log", "debug", "info", "warn", "error", "assert", "dir", "dirxml", - "group", "groupEnd", "time", "timeEnd", "count", "trace", "profile", "profileEnd"]; - - window.console = {}; - for (var i = 0; i < names.length; ++i) - window.console[names[i]] = function() {} -}
\ No newline at end of file diff --git a/wqflask/wqflask/static/new/packages/ValidationPlugin/test/firebug/infoIcon.png b/wqflask/wqflask/static/new/packages/ValidationPlugin/test/firebug/infoIcon.png Binary files differdeleted file mode 100755 index da1e5334..00000000 --- a/wqflask/wqflask/static/new/packages/ValidationPlugin/test/firebug/infoIcon.png +++ /dev/null diff --git a/wqflask/wqflask/static/new/packages/ValidationPlugin/test/firebug/warningIcon.png b/wqflask/wqflask/static/new/packages/ValidationPlugin/test/firebug/warningIcon.png Binary files differdeleted file mode 100755 index de51084e..00000000 --- a/wqflask/wqflask/static/new/packages/ValidationPlugin/test/firebug/warningIcon.png +++ /dev/null diff --git a/wqflask/wqflask/static/new/packages/ValidationPlugin/test/index.html b/wqflask/wqflask/static/new/packages/ValidationPlugin/test/index.html index c2e8ef2b..6f670770 100755..100644 --- a/wqflask/wqflask/static/new/packages/ValidationPlugin/test/index.html +++ b/wqflask/wqflask/static/new/packages/ValidationPlugin/test/index.html @@ -1,300 +1,375 @@ <!DOCTYPE html> <html id="html"> <head> + <meta charset="utf-8"> <title>jQuery - Validation Test Suite</title> - <link rel="Stylesheet" media="screen" href="qunit/qunit.css" /> - <script type="text/javascript" src="jquery.js"></script> - <script type="text/javascript" src="../lib/jquery.form.js"></script> - <script type="text/javascript" src="qunit/qunit.js"></script> - <script type="text/javascript" src="../lib/jquery.metadata.js"></script> - <script type="text/javascript" src="../lib/jquery.mockjax.js"></script> - <script type="text/javascript" src="../jquery.validate.js"></script> - <script type="text/javascript" src="../additional-methods.js"></script> - <script type="text/javascript" src="test.js"></script> - <script type="text/javascript" src="rules.js"></script> - <script type="text/javascript" src="messages.js"></script> - <script type="text/javascript" src="methods.js"></script> + <link rel="stylesheet" href="qunit/qunit.css"> + <script src="../lib/jquery.js"></script> + <script src="../lib/jquery.simulate.js"></script> + <script src="../lib/jquery.form.js"></script> + <script src="qunit/qunit.js"></script> + <script src="../lib/jquery.mockjax.js"></script> + <script src="../dist/jquery.validate.js"></script> + <script src="../dist/additional-methods.js"></script> + <script src="test.js"></script> + <script src="rules.js"></script> + <script src="messages.js"></script> + <script src="methods.js"></script> + <script src="aria.js"></script> + <script src="error-placement.js"></script> </head> <body id="body"> - <h1 id="qunit-header"> - <a href="http://bassistance.de/jquery-plugins/jquery-plugin-validation/">jQuery Validation Plugin</a> Test Suite - <a href="?jquery=1.3.2">jQuery 1.3.2</a> - <a href="?jquery=1.4.2">jQuery 1.4.2</a> - <a href="?jquery=1.4.4">jQuery 1.4.4</a> - <a href="?jquery=1.5.2">jQuery 1.5.2</a> - <a href="?jquery=1.6.1">jQuery 1.6.1</a> - <a href="?jquery=1.7.2">jQuery 1.7.2</a> - <a href="?jquery=git">jQuery Latest (git)</a> - </h1> - <div> +<h1 id="qunit-header"> + <a href="http://jqueryvalidation.org/">jQuery Validation Plugin</a> Test Suite + <a href="?jquery=1.7.2">jQuery 1.7.2</a> + <a href="?jquery=1.8.3">jQuery 1.8.3</a> + <a href="?jquery=1.9.1">jQuery 1.9.1</a> + <a href="?jquery=1.11.1">jQuery 1.11.1</a> + <a href="?jquery=git">jQuery Latest (git)</a> +</h1> +<div> +</div> +<h2 id="qunit-banner"></h2> +<div id="qunit-testrunner-toolbar"></div> +<h2 id="qunit-userAgent"></h2> +<ol id="qunit-tests"></ol> +<!-- Test HTML --> +<div id="other" style="display:none;"> + <input type="password" name="pw1" id="pw1" value="engfeh"> + <input type="password" name="pw2" id="pw2" value=""> +</div> +<div id="qunit-fixture"> + <p id="firstp">See <a id="simon1" href="http://simon.incutio.com/archive/2003/03/25/#getElementsBySelector" rel="bookmark">this blog entry</a> for more information.</p> + <p id="ap"> + Here are some links in a normal paragraph: <a id="google" href="http://www.google.com/" title="Google!">Google</a>, + <a id="groups" href="http://groups.google.com/">Google Groups</a>. This link has + <code><a href="#" id="anchor1">class="blog"</a> + </code>: + <a href="http://diveintomark.org/" class="blog" hreflang="en" id="mark">diveintomark</a> + </p> + <div id="foo"> + <p id="sndp">Everything inside the red border is inside a div with + <code>id="foo"</code>.</p> + <p lang="en" id="en">This is a normal link: <a id="yahoo" href="http://www.yahoo.com/" class="blogTest">Yahoo</a> + </p> + <p id="sap">This link has + <code><a href="#2" id="anchor2">class="blog"</a> + </code>: <a href="http://simon.incutio.com/" class="blog link" id="simon">Simon Willison's Weblog</a> + </p> </div> - <h2 id="qunit-banner"></h2> - <div id="qunit-testrunner-toolbar"></div> - <h2 id="qunit-userAgent"></h2> - <ol id="qunit-tests"></ol> - - <!-- Test HTML --> - <div id="other" style="display:none;"> - <input type="password" name="pw1" id="pw1" value="engfeh" /> - <input type="password" name="pw2" id="pw2" value="" /> + <p id="first">Try them out:</p> + <ul id="firstUL"></ul> + <ol id="empty"></ol> + <form id="testForm1"> + <input type="text" data-rule-required="true" data-rule-minlength="2" title="buga" name="firstname" id="firstname"> + <label id="errorFirstname" for="firstname" class="error">error for firstname</label> + <input type="text" data-rule-required="true" title="buga" name="lastname" id="lastname"> + <input type="text" data-rule-required="true" title="something" name="something" id="something" value="something"> + </form> + <form id="testForm1clean"> + <input title="buga" name="firstnamec" id="firstnamec"> + <label id="errorFirstnamec" for="firstnamec" class="error">error for firstname</label> + <input title="buga" name="lastname" id="lastnamec"> + <input name="username" id="usernamec"> + </form> + <form id="userForm"> + <input type="text" data-rule-required="true" name="username" id="username"> + <input type="submit" name="submitButton" value="submitButtonValue"> + </form> + <form id="signupForm" action="form.php"> + <input id="user" name="user" title="Please enter your username (at least 3 characters)" data-rule-required="true" data-rule-minlength="3"> + <input type="password" name="password" id="password" data-rule-required="true" data-rule-minlength="5"> + </form> + <form id="testForm2"> + <input data-rule-required="true" type="radio" name="agree" id="agb"> + <label for="agree" id="agreeLabel" class="xerror">error for agb</label> + </form> + <form id="testForm3"> + <select data-rule-required="true" name="meal" id="meal"> + <option value="">Please select...</option> + <option value="1">Food</option> + <option value="2">Milk</option> + </select> + </form> + <div class="error" id="errorContainer"> + <ul> + <li class="error" id="errorWrapper"> + <label for="meal" id="mealLabel" class="error">error for meal</label> + </li> + </ul> </div> - <div id="qunit-fixture"> - <p id="firstp">See <a id="simon1" href="http://simon.incutio.com/archive/2003/03/25/#getElementsBySelector" rel="bookmark">this blog entry</a> for more information.</p> - <p id="ap"> - Here are some links in a normal paragraph: <a id="google" href="http://www.google.com/" title="Google!">Google</a>, - <a id="groups" href="http://groups.google.com/">Google Groups</a>. - This link has <code><a href="#" id="anchor1">class="blog"</a></code>: - <a href="http://diveintomark.org/" class="blog" hreflang="en" id="mark">diveintomark</a> - - </p> - <div id="foo"> - <p id="sndp">Everything inside the red border is inside a div with <code>id="foo"</code>.</p> - <p lang="en" id="en">This is a normal link: <a id="yahoo" href="http://www.yahoo.com/" class="blogTest">Yahoo</a></p> - <p id="sap">This link has <code><a href="#2" id="anchor2">class="blog"</a></code>: <a href="http://simon.incutio.com/" class="blog link" id="simon">Simon Willison's Weblog</a></p> - - </div> - <p id="first">Try them out:</p> - <ul id="firstUL"></ul> - <ol id="empty"></ol> - - <form id="testForm1"> - <input type="text" class="{required:true,minlength:2}" title="buga" name="firstname" id="firstname" /> - <label id="errorFirstname" for="firstname" class="error">error for firstname</label> - <input type="text" class="{required:true}" title="buga" name="lastname" id="lastname" /> - <input type="text" class="{required:true}" title="something" name="something" id="something" value="something" /> - </form> - - <form id="testForm1clean"> - <input title="buga" name="firstname" id="firstnamec" /> - <label id="errorFirstname" for="firstname" class="error">error for firstname</label> - <input title="buga" name="lastname" id="lastnamec" /> - <input name="username" id="usernamec" /> - </form> - - <form id="userForm"> - <input type="text" class="{required:true}" name="username" id="username" /> - <input type="submit" name="submitButton" value="submitButtonValue" /> - </form> - - <form id="signupForm" action="form.php"> - <input id="user" name="user" title="Please enter your username (at least 3 characters)" class="{required:true,minlength:3}" /> - <input type="password" name="password" id="password" class="{required:true,minlength:5}" /> - </form> - - <form id="testForm2"> - <input class="{required:true}" type="radio" name="agree" id="agb" /> - <label for="agree" id="agreeLabel" class="xerror">error for agb</label> - </form> - - <form id="testForm3"> - <select class="{required:true}" name="meal" id="meal" > - <option value="">Please select...</option> - <option value="1">Food</option> - <option value="2">Milk</option> - </select> - </form> - <div class="error" id="errorContainer"> - <ul> - <li class="error" id="errorWrapper"> - <label for="meal" id="mealLabel" class="error">error for meal</label> - </li> - </ul> + <form id="testForm4"> + <input data-rule-foo="true" name="f1" id="f1"> + <input data-rule-bar="true" name="f2" id="f2"> + </form> + <form id="testForm5"> + <input data-rule-equalto="#x2" value="x" name="x1" id="x1"> + <input data-rule-equalto="#x1" value="y" name="x2" id="x2"> + </form> + <form id="testForm6"> + <input data-rule-required="true" data-rule-minlength="2" type="checkbox" name="check" id="form6check1"> + <input type="checkbox" name="check" id="form6check2"> + </form> + <form id="testForm7"> + <select data-rule-required="true" data-rule-minlength="2" name="selectf7" id="selectf7" multiple="multiple"> + <option id="optionxa" value="0">0</option> + <option id="optionxb" value="1">1</option> + <option id="optionxc" value="2">2</option> + <option id="optionxd" value="3">3</option> + </select> + </form> + <form id="dateRangeForm"> + <input id="fromDate" name="fromDate" class="requiredDateRange" value="x"> + <input id="toDate" name="toDate" class="requiredDateRange" value="y"> + <span class="errorContainer"></span> + </form> + <form id="testForm8"> + <input id="form8input" data-rule-required="true" data-rule-number="true" data-rule-rangelength="2,8" name="abc"> + <input type="radio" name="radio1"> + </form> + <form id="testForm9"> + <input id="testEmail9" data-rule-required="true" data-rule-email="true" data-msg-required="required" data-msg-email="email"> + <input id="testGeneric9" data-rule-required="true" data-rule-email="true" data-msg="generic" data-msg-email="email"> + </form> + <form id="testForm10"> + <input type="radio" name="testForm10Radio" value="1" id="testForm10Radio1"> + <input type="radio" name="testForm10Radio" value="2" id="testForm10Radio2"> + </form> + <form id="testForm11"> + <!-- HTML5 --> + <input required type="text" name="testForm11Text" id="testForm11text1"> + </form> + <form id="testForm12"> + <!-- empty "type" attribute --> + <input name="testForm12text" id="testForm12text" data-rule-required="true"> + </form> + <form id="testForm13"> + <select id="cars-select" name="cars" title="Please select at least two cars, but no more than three" required rangelength="[2,3]" multiple="multiple"> + <option value="m_sl">Mercedes SL</option> + <option value="o_c">Opel Corsa</option> + <option value="vw_p">VW Polo</option> + <option value="t_s">Titanic Skoda</option> + </select> + </form> + <form id="testForm14"> + <!-- test existing "label" error holder --> + <input name="testForm14text" id="testForm14text" data-rule-required="true" data-msg="required"> + <label for="testForm14text" class="error"></label> + </form> + <form id="testForm16"> + <!-- test existing "label" attribute --> + <input name="testForm16text" id="testForm16text" data-rule-required="true" data-msg="missing"> + <label for="testForm16text" class="title">Field Label</label> + </form> + <form id="testForm15"> + <!-- test existing non-label error holder --> + <input name="testForm15text" id="testForm15text" data-rule-required="true" data-msg="required" aria-describedby="testForm15text-error"> + <span id="testForm15text-error" class="error"></span> + </form> + <form id="testForm17"> + <!-- test existing non-error aria-describedby --> + <label for="testForm17text">My Label</label> + <input name="testForm17text" id="testForm17text" data-rule-required="true" data-msg="required" aria-describedby="testForm17text-description"> + <span id="testForm17text-description">This is where you enter your data</span> + </form> + <form id="testForm18"> + <!-- test id/name containing brackets --> + <input name="testForm18[text]" id="testForm18[text]" required> + </form> + <form id="testForm19"> + <!-- test id/name containing $ --> + <input name="testForm19$text" id="testForm19$text" required> + </form> + <form id="dataMessages"> + <input name="dataMessagesName" id="dataMessagesName" class="required" data-msg-required="You must enter a value here"> + </form> + <div id="simplecontainer"> + <h3></h3> + </div> + <div id="container" style="min-height:1px"></div> + <ol id="labelcontainer"></ol> + <form id="elementsOrder"> + <select class="required" name="order1" id="order1"> + <option value="">none</option> + </select> + <input class="required" name="order2" id="order2"> + <input class="required" name="order3" type="checkbox" id="order3"> + <input class="required" name="order4" id="order4"> + <input class="required" name="order5" type="radio" id="order5"> + <input class="required" name="order6" id="order6"> + <ul id="orderContainer"> + </ul> + </form> + <form id="form" action="formaction"> + <input type="text" name="action" value="Test" id="text1"> + <input type="text" name="text2" value="" id="text1b"> + <input type="text" name="text2" value="T " id="text1c"> + <input type="text" name="text2" value="T" id="text2"> + <input type="text" name="text2" value="TestTestTest" id="text3"> + <input type="text" name="action" value="0" id="value1"> + <input type="text" name="text2" value="10" id="value2"> + <input type="text" name="text2" value="1000" id="value3"> + <input type="radio" name="radio1" id="radio1"> + <input type="radio" name="radio1" id="radio1a"> + <input type="radio" name="radio2" id="radio2" checked="checked"> + <input type="radio" name="radio" id="radio3"> + <input type="radio" name="radio" id="radio4" checked="checked"> + <input type="checkbox" name="check" id="check1" checked="checked"> + <input type="checkbox" name="check" id="check1b"> + <input type="checkbox" name="check2" id="check2"> + <input type="checkbox" name="check3" id="check3" checked="checked"> + <input type="checkbox" name="check3" checked="checked"> + <input type="checkbox" name="check3" checked="checked"> + <input type="checkbox" name="check3" checked="checked"> + <input type="checkbox" name="check3" checked="checked"> + <input type="hidden" name="hidden" id="hidden1"> + <input type="text" style="display:none;" name="foo[bar]" id="hidden2"> + <input type="text" readonly="readonly" id="name" name="name" value="name"> + <button name="button">Button</button> + <textarea id="area1" name="area1">foobar</textarea> + <textarea id="area2" name="area2"></textarea> + <select name="select1" id="select1"> + <option id="option1a" value="">Nothing</option> + <option id="option1b" value="1">1</option> + <option id="option1c" value="2">2</option> + <option id="option1d" value="3">3</option> + </select> + <select name="select2" id="select2"> + <option id="option2a" value="">Nothing</option> + <option id="option2b" value="1">1</option> + <option id="option2c" value="2">2</option> + <option id="option2d" selected="selected" value="3">3</option> + </select> + <select name="select3" id="select3" multiple="multiple"> + <option id="option3a" value="">Nothing</option> + <option id="option3b" selected="selected" value="1">1</option> + <option id="option3c" selected="selected" value="2">2</option> + <option id="option3d" value="3">3</option> + </select> + <select name="select4" id="select4" multiple="multiple"> + <option id="option4a" selected="selected" value="1">1</option> + <option id="option4b" selected="selected" value="2">2</option> + <option id="option4c" selected="selected" value="3">3</option> + <option id="option4d" selected="selected" value="4">4</option> + <option id="option4e" selected="selected" value="5">5</option> + </select> + <select name="select5" id="select5" multiple="multiple"> + <option id="option5a" value="0">0</option> + <option id="option5b" value="1">1</option> + <option id="option5c" value="2">2</option> + <option id="option5d" value="3">3</option> + </select> + </form> + <form id="v2"> + <input id="v2-i1" name="v2-i1" class="required"> + <input id="v2-i2" name="v2-i2" class="required email"> + <input id="v2-i3" name="v2-i3" class="url"> + <input id="v2-i4" name="v2-i4" class="required" minlength="2"> + <input id="v2-i5" name="v2-i5" class="required" minlength="2" maxlength="5" customMethod1="123"> + <input id="v2-i6" name="v2-i6" class="required customMethod2" data-rule-maxlength="5" data-rule-minlength="2"> + <input id="v2-i7" name="v2-i7"> + </form> + <form id="checkables"> + <input type="checkbox" id="checkable1" name="checkablesgroup" class="required"> + <input type="checkbox" id="checkable2" name="checkablesgroup"> + <input type="checkbox" id="checkable3" name="checkablesgroup"> + </form> + <form id="subformRequired"> + <div class="billingAddressControl"> + <input type="checkbox" id="bill_to_co" name="bill_to_co" class="toggleCheck" checked="checked" style="width: auto;" tabindex="1"> + <label for="bill_to_co" style="cursor:pointer">Same as Company Address</label> </div> - - <form id="testForm4"> - <input class="{foo:true}" name="f1" id="f1" /> - <input class="{bar:true}" name="f2" id="f2" /> - </form> - - <form id="testForm5"> - <input class="{equalTo:'#x2'}" value="x" name="x1" id="x1" /> - <input class="{equalTo:'#x1'}" value="y" name="x2" id="x2" /> - </form> - - <form id="testForm6"> - <input class="{required:true,minlength:2}" type="checkbox" name="check" id="form6check1" /> - <input type="checkbox" name="check" id="form6check2" /> - </form> - - <form id="testForm7"> - <select class="{required:true,minlength:2}" name="selectf7" id="selectf7" multiple="multiple"> - <option id="optionxa" value="0">0</option> - <option id="optionxb" value="1">1</option> - <option id="optionxc" value="2">2</option> - <option id="optionxd" value="3">3</option> - </select> - </form> - - <form id="dateRangeForm"> - <input id="fromDate" name="fromDate" class="requiredDateRange" value="x" /> - <input id="toDate" name="toDate" class="requiredDateRange" value="y" /> - <span class="errorContainer"></span> - </form> - - <form id="testForm8"> - <input id="form8input" class="{required:true,number:true,rangelength:[2,8]}" name="abc" /> - <input type="radio" name="radio1"/> - </form> - - <form id="testForm9"> - <input id="testEmail9" class="{required:true,email:true,messages:{required:'required',email:'email'}}" /> - </form> - - <form id="testForm10"> - <input type="radio" name="testForm10Radio" value="1" id="testForm10Radio1" /> - <input type="radio" name="testForm10Radio" value="2" id="testForm10Radio2" /> - </form> - - <form id="testForm11"> - <!-- HTML5 --> - <input required type="text" name="testForm11Text" id="testForm11text1" /> - </form> - - <form id="testForm12"> - <!-- empty "type" attribute --> - <input name="testForm12text" id="testForm12text" class="{required:true}" /> - </form> - - <form id="dataMessages"> - <input name="dataMessagesName" id="dataMessagesName" class="required" data-msg-required="You must enter a value here" /> - </form> - - <div id="simplecontainer"> - <h3></h3> + <div id="subform"> + <input maxlength="40" class="billingRequired" name="bill_first_name" size="20" type="text" tabindex="2" value=""> </div> - - <div id="container"></div> - - <ol id="labelcontainer"></ol> - - <form id="elementsOrder"> - <select class="required" name="order1" id="order1"><option value="">none</option></select> - <input class="required" name="order2" id="order2"/> - <input class="required" name="order3" type="checkbox" id="order3"/> - <input class="required" name="order4" id="order4"/> - <input class="required" name="order5" type="radio" id="order5"/> - <input class="required" name="order6" id="order6"/> - <ul id="orderContainer"> - </ul> - </form> - - <form id="form" action="formaction"> - <input type="text" name="action" value="Test" id="text1"/> - <input type="text" name="text2" value=" " id="text1b"/> - <input type="text" name="text2" value="T " id="text1c"/> - <input type="text" name="text2" value="T" id="text2"/> - <input type="text" name="text2" value="TestTestTest" id="text3"/> - - <input type="text" name="action" value="0" id="value1"/> - <input type="text" name="text2" value="10" id="value2"/> - <input type="text" name="text2" value="1000" id="value3"/> - - <input type="radio" name="radio1" id="radio1"/> - <input type="radio" name="radio1" id="radio1a"/> - <input type="radio" name="radio2" id="radio2" checked="checked"/> - <input type="radio" name="radio" id="radio3"/> - <input type="radio" name="radio" id="radio4" checked="checked"/> - - <input type="checkbox" name="check" id="check1" checked="checked"/> - <input type="checkbox" name="check" id="check1b" /> - - <input type="checkbox" name="check2" id="check2"/> - - <input type="checkbox" name="check3" id="check3" checked="checked"/> - <input type="checkbox" name="check3" checked="checked"/> - <input type="checkbox" name="check3" checked="checked"/> - <input type="checkbox" name="check3" checked="checked"/> - <input type="checkbox" name="check3" checked="checked"/> - - <input type="hidden" name="hidden" id="hidden1"/> - <input type="text" style="display:none;" name="foo[bar]" id="hidden2"/> - - <input type="text" readonly="readonly" id="name" name="name" value="name" /> - - <button name="button">Button</button> - - <textarea id="area1" name="area1">foobar</textarea> - - - <textarea id="area2" name="area2"></textarea> - - <select name="select1" id="select1"> - <option id="option1a" value="">Nothing</option> - <option id="option1b" value="1">1</option> - <option id="option1c" value="2">2</option> - <option id="option1d" value="3">3</option> - </select> - <select name="select2" id="select2"> - <option id="option2a" value="">Nothing</option> - <option id="option2b" value="1">1</option> - <option id="option2c" value="2">2</option> - <option id="option2d" selected="selected" value="3">3</option> - </select> - <select name="select3" id="select3" multiple="multiple"> - <option id="option3a" value="">Nothing</option> - <option id="option3b" selected="selected" value="1">1</option> - <option id="option3c" selected="selected" value="2">2</option> - <option id="option3d" value="3">3</option> - </select> - <select name="select4" id="select4" multiple="multiple"> - <option id="option4a" selected="selected" value="1">1</option> - <option id="option4b" selected="selected" value="2">2</option> - <option id="option4c" selected="selected" value="3">3</option> - <option id="option4d" selected="selected" value="4">4</option> - <option id="option4e" selected="selected" value="5">5</option> - </select> - <select name="select5" id="select5" multiple="multiple"> - <option id="option5a" value="0">0</option> - <option id="option5b" value="1">1</option> - <option id="option5c" value="2">2</option> - <option id="option5d" value="3">3</option> - </select> - </form> - - <form id="v2"> - <input id="v2-i1" name="v2-i1" class="required" /> - <input id="v2-i2" name="v2-i2" class="required email" /> - <input id="v2-i3" name="v2-i3" class="url" /> - <input id="v2-i4" name="v2-i4" class="required" minlength="2" /> - <input id="v2-i5" name="v2-i5" class="required" minlength="2" maxlength="5" customMethod1="123" /> - <input id="v2-i6" name="v2-i6" class="required customMethod2 {maxlength: 5}" minlength="2" /> - <input id="v2-i7" name="v2-i7" /> - </form> - - <form id="checkables"> - <input type="checkbox" id="checkable1" name="checkablesgroup" class="required" /> - <input type="checkbox" id="checkable2" name="checkablesgroup" /> - <input type="checkbox" id="checkable3" name="checkablesgroup" /> - </form> - - - <form id="subformRequired"> - <div class="billingAddressControl"> - <input type="checkbox" id="bill_to_co" name="bill_to_co" class="toggleCheck" checked="checked" style="width: auto;" tabindex="1" /> - <label for="bill_to_co" style="cursor:pointer">Same as Company Address</label> - </div> - <div id="subform"> - <input maxlength="40" class="billingRequired" name="bill_first_name" size="20" type="text" tabindex="2" value="" /> - </div> - <input id="co_name" class="required" maxlength="40" name="co_name" size="20" type="text" tabindex="1" value="" /> - </form> - - <form id="withTitle"> - <input class="required" name="hastitle" type="text" title="fromtitle" /> - </form> - - <form id="ccform" method="get" action=""> - <input id="cardnumber" name="cardnumber" /> - </form> - - <form id="productInfo"> - <input class="productInfo" name="partnumber"> - <input class="productInfo" name="description"> - <input class="productInfo" name="color"> - <input class="productInfo" type="checkbox" name="discount" /> - </form> - - </div> - + <input id="co_name" class="required" maxlength="40" name="co_name" size="20" type="text" tabindex="1" value=""> + </form> + <form id="withTitle"> + <input class="required" name="hastitle" type="text" title="fromtitle"> + </form> + <form id="ccform" method="get"> + <input id="cardnumber" name="cardnumber"> + </form> + <form id="productInfo"> + <input class="productInfo" name="partnumber"> + <input class="productInfo" name="description"> + <input class="productInfo" name="color"> + <input name="supplier"> + <input class="productInfo" type="checkbox" name="discount"> + </form> + <form id="updateLabel"> + <input class="required" name="updateLabelInput" id="updateLabelInput" data-msg-required="You must enter a value here"> + <label id="targetLabel" class="error" for="updateLabelInput">Some server-side error</label> + </form> + <form id="rangesMinDateInvalid"> + <input type="date" id="minDateInvalid" name="minDateInvalid" min="2012-12-21" value="2012-11-21"> + </form> + <form id="ranges"> + <input type="date" id="maxDateInvalid" name="maxDateInvalid" max="2012-12-21" value="2013-01-21"> + <input type="date" id="rangeDateInvalidGreater" name="rangeDateInvalidGreater" min="2012-11-21" max="2013-01-21" value="2013-02-21"> + <input type="date" id="rangeDateInvalidLess" name="rangeDateInvalidLess" min="2012-11-21" max="2013-01-21" value="2012-10-21"> + <input type="date" id="maxDateValid" name="maxDateValid" max="2013-01-21" value="2012-12-21"> + <input type="date" id="rangeDateValid" name="rangeDateValid" min="2012-11-21" max="2013-01-21" value="2012-12-21"> + <!-- input type text is not supposed to have min/max according to html5, + but for backward compatibility with 1.10.0 we treat it as number. + you can also use type="number", in which case the browser may also + do validation, and mobile browsers may offer a numeric keypad to edit + the value. + Type absent is treated like type="text". + --> + <input type="text" id="rangeTextInvalidGreater" name="rangeTextInvalidGreater" min="50" max="200" value="1000"> + <input type="text" id="rangeTextInvalidLess" name="rangeTextInvalidLess" min="200" max="1000" value="50"> + <input id="rangeAbsentInvalidGreater" name="rangeAbsentInvalidGreater" min="50" max="200" value="1000"> + <input id="rangeAbsentInvalidLess" name="rangeAbsentInvalidLess" min="200" max="1000" value="50"> + <input type="text" id="rangeTextValid" name="rangeTextValid" min="50" max="1000" value="200"> + <input type="text" id="rangeTextDataRuleValid" name="rangeTextValid" min="50" data-rule-max="1000.00" value="200"> + <input id="rangeAbsentValid" name="rangeAbsentValid" min="50" max="1000" value="200"> + <!-- ranges are like numbers in html5, except that browser is not required + to demand an exact value. User interface could be a slider. + --> + <input type="range" id="rangeRangeValid" name="rangeRangeValid" min="50" max="1000" value="200"> + <input type="number" id="rangeNumberValid" name="rangeNumberValid" min="50" max="1000" value="200"> + <input type="number" id="rangeNumberInvalidGreater" name="rangeNumberInvalidGreater" min="50" max="200" value="1000"> + <input type="number" id="rangeNumberInvalidLess" name="rangeNumberInvalidLess" min="50" max="200" value="6"> + <input type="number" id="rangeMinZeroInvalidLess" name="rangeMinZeroInvalidLess" min="0" value="-1"> + <input type="number" id="rangeMinZeroValidEqual" name="rangeMinZeroValidEqual" min="0" value="0"> + <input type="number" id="rangeMinZeroValidGreater" name="rangeMinZeroValidGreater" min="0" value="1"> + </form> + <form id="rangeMinDateValid"> + <input type="date" id="minDateValid" name="minDateValid" min="2012-11-21" value="2012-12-21"> + </form> + <form id="bypassValidation"> + <input type="text" name="bypassValidationRequiredInput" required> + <input id="normalSubmit" type="submit" value="submit"> + <input id="bypassSubmitWithCancel" type="submit" class="cancel" value="bypass1"> + <input id="bypassSubmitWithNoValidate1" type="submit" formnovalidate value="bypass1"> + <input id="bypassSubmitWithNoValidate2" type="submit" formnovalidate="formnovalidate" value="bypass2"> + </form> + <form id="ariaInvalid"> + <input type="text" name="ariaInvalidFirstName" id="ariaInvalidFirstName"> + </form> + <form id="ariaRequired"> + <input type="text" name="ariaRequiredDynamic" id="ariaRequiredDynamic"> + <input type="text" name="ariaRequiredStatic" id="ariaRequiredStatic" required=""> + <input type="text" name="ariaRequiredData" id="ariaRequiredData" data-rule-required="true"> + <input type="text" name="ariaRequiredClass" id="ariaRequiredClass" class="required"> + </form> + <form id="ignoredElements"> + <select id="ss1" class="ignore"> + <option value="1">option 1</option> + <option value="2">option 2</option> + </select> + <br> + <input name="test" class="required" value=""> + </form> + <form id="radiocheckbox" autocomplete="off"> + <input id="radiocheckbox-0-1" autocomplete="off" type="radio" name="radiocheckbox-0" required="required"> + <input id="radiocheckbox-0-2" autocomplete="off" type="radio" name="radiocheckbox-0" required="required"> + <input id="radiocheckbox-0-3" autocomplete="off" type="radio" name="radiocheckbox-0" required="required"> + <input id="radiocheckbox-1-1" autocomplete="off" type="checkbox" name="radiocheckbox-1" required="required"> + <input id="radiocheckbox-1-2" autocomplete="off" type="checkbox" name="radiocheckbox-1" required="required"> + <input id="radiocheckbox-1-3" autocomplete="off" type="checkbox" name="radiocheckbox-1" required="required"> + </form> +</div> </body> </html> diff --git a/wqflask/wqflask/static/new/packages/ValidationPlugin/test/jquery.js b/wqflask/wqflask/static/new/packages/ValidationPlugin/test/jquery.js deleted file mode 100755 index 100c4465..00000000 --- a/wqflask/wqflask/static/new/packages/ValidationPlugin/test/jquery.js +++ /dev/null @@ -1,25 +0,0 @@ -(function() { - -var parts = document.location.search.slice( 1 ).split( "&" ), - length = parts.length, - i = 0, - current, - version = "1.3.2", - file = "http://code.jquery.com/jquery-git.js"; - -for ( ; i < length; i++ ) { - current = parts[ i ].split( "=" ); - if ( current[ 0 ] === "jquery" ) { - version = current[ 1 ]; - break; - } -} - -if (version != "git") { - file = "../lib/jquery-" + version + ".js"; -} - - -document.write( "<script src='" + file + "'></script>" ); - -})(); diff --git a/wqflask/wqflask/static/new/packages/ValidationPlugin/test/large.html b/wqflask/wqflask/static/new/packages/ValidationPlugin/test/large.html deleted file mode 100755 index 9e8a0e85..00000000 --- a/wqflask/wqflask/static/new/packages/ValidationPlugin/test/large.html +++ /dev/null @@ -1,188 +0,0 @@ -<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> -<html xmlns="http://www.w3.org/1999/xhtml"> -<head> -<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1" /> -<title>Test for jQuery validate() plugin</title> - -<link rel="stylesheet" type="text/css" media="screen" href="css/screen.css" /> -<script src="../lib/jquery.js" type="text/javascript"></script> -<script src="../lib/jquery.metadata.js" type="text/javascript"></script> -<script src="../lib/jquery.ajaxQueue.js" type="text/javascript"></script> -<script src="../jquery.validate.js" type="text/javascript"></script> - -<script type="text/javascript"> -$().ready(function() { - $("#commentForm").validate(); -}); -</script> - -<style type="text/css"> -#commentForm { width: 500px; } -#commentForm label { width: 250px; display: block; float: left; } -#commentForm label.error, #commentForm input.submit { margin-left: 253px; } -.focus { background-color: red; } -</style> - -</head> -<body> -<form class="cmxform" id="commentForm" method="get" action=""> - <fieldset> - <legend>A simple comment form with submit validation and default messages</legend> - <p> - <label for="cname-x0">Name (required, at least 2 characters)</label> - <input id="cname-x0" name="name-x0" class="some other styles {required:true,minLength:2}" /> - <p> - <label for="cemail-x0">E-Mail (required)</label> - <input id="cemail-x0" name="email-x0" class="{required:true,email:true}" /> - </p> - <p> - <label for="curl-x0">URL (optional)</label> - <input id="curl-x0" name="url-x0" class="{url:true}" value="" /> - </p> - <p> - <label for="ccomment-x0">Your comment (required)</label> - <textarea id="ccomment-x0" name="comment-x0" class="{required:true}"></textarea> - </p> - <p> - <label for="cname-x1">Name (required, at least 2 characters)</label> - <input class="some other styles {required:true,minLength:2}" name="name-x1" id="cname-x1"/> - </p><p> - <label for="cemail-x1">E-Mail (required)</label> - <input class="{required:true,email:true}" name="email-x1" id="cemail-x1"/> - </p> - <p> - <label for="curl-x1">URL (optional)</label> - <input value="" class="{url:true}" name="url-x1" id="curl-x1"/> - </p> - <p> - <label for="ccomment-x1">Your comment (required)</label> - <textarea class="{required:true}" name="comment-x1" id="ccomment-x1"></textarea> - </p> - <p> - <label for="cname-x2">Name (required, at least 2 characters)</label> - <input class="some other styles {required:true,minLength:2}" name="name-x2" id="cname-x2"/> - </p><p> - <label for="cemail-x2">E-Mail (required)</label> - <input class="{required:true,email:true}" name="email-x2" id="cemail-x2"/> - </p> - <p> - <label for="curl-x2">URL (optional)</label> - <input value="" class="{url:true}" name="url-x2" id="curl-x2"/> - </p> - <p> - <label for="ccomment-x2">Your comment (required)</label> - <textarea class="{required:true}" name="comment-x2" id="ccomment-x2"></textarea> - </p> - <p> - <label for="cname-x3">Name (required, at least 2 characters)</label> - <input class="some other styles {required:true,minLength:2}" name="name-x3" id="cname-x3"/> - </p><p> - <label for="cemail-x3">E-Mail (required)</label> - <input class="{required:true,email:true}" name="email-x3" id="cemail-x3"/> - </p> - <p> - <label for="curl-x3">URL (optional)</label> - <input value="" class="{url:true}" name="url-x3" id="curl-x3"/> - </p> - <p> - <label for="ccomment-x3">Your comment (required)</label> - <textarea class="{required:true}" name="comment-x3" id="ccomment-x3"></textarea> - </p> - <p> - <label for="cname-x4">Name (required, at least 2 characters)</label> - <input class="some other styles {required:true,minLength:2}" name="name-x4" id="cname-x4"/> - </p><p> - <label for="cemail-x4">E-Mail (required)</label> - <input class="{required:true,email:true}" name="email-x4" id="cemail-x4"/> - </p> - <p> - <label for="curl-x4">URL (optional)</label> - <input value="" class="{url:true}" name="url-x4" id="curl-x4"/> - </p> - <p> - <label for="ccomment-x4">Your comment (required)</label> - <textarea class="{required:true}" name="comment-x4" id="ccomment-x4"></textarea> - </p> - <p> - <label for="cname-x5">Name (required, at least 2 characters)</label> - <input class="some other styles {required:true,minLength:2}" name="name-x5" id="cname-x5"/> - </p><p> - <label for="cemail-x5">E-Mail (required)</label> - <input class="{required:true,email:true}" name="email-x5" id="cemail-x5"/> - </p> - <p> - <label for="curl-x5">URL (optional)</label> - <input value="" class="{url:true}" name="url-x5" id="curl-x5"/> - </p> - <p> - <label for="ccomment-x5">Your comment (required)</label> - <textarea class="{required:true}" name="comment-x5" id="ccomment-x5"></textarea> - </p> - <p> - <label for="cname-x6">Name (required, at least 2 characters)</label> - <input class="some other styles {required:true,minLength:2}" name="name-x6" id="cname-x6"/> - </p><p> - <label for="cemail-x6">E-Mail (required)</label> - <input class="{required:true,email:true}" name="email-x6" id="cemail-x6"/> - </p> - <p> - <label for="curl-x6">URL (optional)</label> - <input value="" class="{url:true}" name="url-x6" id="curl-x6"/> - </p> - <p> - <label for="ccomment-x6">Your comment (required)</label> - <textarea class="{required:true}" name="comment-x6" id="ccomment-x6"></textarea> - </p> - <p> - <label for="cname-x7">Name (required, at least 2 characters)</label> - <input class="some other styles {required:true,minLength:2}" name="name-x7" id="cname-x7"/> - </p><p> - <label for="cemail-x7">E-Mail (required)</label> - <input class="{required:true,email:true}" name="email-x7" id="cemail-x7"/> - </p> - <p> - <label for="curl-x7">URL (optional)</label> - <input value="" class="{url:true}" name="url-x7" id="curl-x7"/> - </p> - <p> - <label for="ccomment-x7">Your comment (required)</label> - <textarea class="{required:true}" name="comment-x7" id="ccomment-x7"></textarea> - </p> - <p> - <label for="cname-x8">Name (required, at least 2 characters)</label> - <input class="some other styles {required:true,minLength:2}" name="name-x8" id="cname-x8"/> - </p><p> - <label for="cemail-x8">E-Mail (required)</label> - <input class="{required:true,email:true}" name="email-x8" id="cemail-x8"/> - </p> - <p> - <label for="curl-x8">URL (optional)</label> - <input value="" class="{url:true}" name="url-x8" id="curl-x8"/> - </p> - <p> - <label for="ccomment-x8">Your comment (required)</label> - <textarea class="{required:true}" name="comment-x8" id="ccomment-x8"></textarea> - </p> - <p> - <label for="cname-x9">Name (required, at least 2 characters)</label> - <input class="some other styles {required:true,minLength:2}" name="name-x9" id="cname-x9"/> - </p><p> - <label for="cemail-x9">E-Mail (required)</label> - <input class="{required:true,email:true}" name="email-x9" id="cemail-x9"/> - </p> - <p> - <label for="curl-x9">URL (optional)</label> - <input value="" class="{url:true}" name="url-x9" id="curl-x9"/> - </p> - <p> - <label for="ccomment-x9">Your comment (required)</label> - <textarea class="{required:true}" name="comment-x9" id="ccomment-x9"></textarea> - </p> - <p> - <input class="submit" type="submit" value="Submit"/> - </p> - </fieldset> -</form> - -</body> -</html> diff --git a/wqflask/wqflask/static/new/packages/ValidationPlugin/test/messages.js b/wqflask/wqflask/static/new/packages/ValidationPlugin/test/messages.js index 5f6277af..10b4fde8 100755..100644 --- a/wqflask/wqflask/static/new/packages/ValidationPlugin/test/messages.js +++ b/wqflask/wqflask/static/new/packages/ValidationPlugin/test/messages.js @@ -11,7 +11,7 @@ test("predefined message not overwritten by addMethod(a, b, undefined)", functio test("group error messages", function() { $.validator.addClassRules({ - requiredDateRange: {required:true, date:true, dateRange:true} + requiredDateRange: { required: true, date: true, dateRange: true } }); $.validator.addMethod("dateRange", function() { return new Date($("#fromDate").val()) < new Date($("#toDate").val()); @@ -27,35 +27,42 @@ test("group error messages", function() { }); ok( !form.valid() ); equal( 1, form.find(".errorContainer *").length ); - equal( "Please enter a valid date.", form.find(".errorContainer label.error").text() ); + equal( "Please enter a valid date.", form.find(".errorContainer .error:not(input)").text() ); $("#fromDate").val("12/03/2006"); $("#toDate").val("12/01/2006"); ok( !form.valid() ); - equal( "Please specify a correct date range.", form.find(".errorContainer label.error").text() ); + equal( "Please specify a correct date range.", form.find(".errorContainer .error:not(input)").text() ); $("#toDate").val("12/04/2006"); ok( form.valid() ); - ok( form.find(".errorContainer label.error").is(":hidden") ); + ok( form.find(".errorContainer .error:not(input)").is(":hidden") ); }); test("read messages from metadata", function() { - var form = $("#testForm9") + var form = $("#testForm9"), + e, g; + form.validate(); - var e = $("#testEmail9") + e = $("#testEmail9"); e.valid(); - equal( form.find("label").text(), "required" ); + equal( form.find("#testEmail9").next(".error:not(input)").text(), "required" ); e.val("bla").valid(); - equal( form.find("label").text(), "email" ); -}); + equal( form.find("#testEmail9").next(".error:not(input)").text(), "email" ); + g = $("#testGeneric9"); + g.valid(); + equal( form.find("#testGeneric9").next(".error:not(input)").text(), "generic"); + g.val("bla").valid(); + equal( form.find("#testGeneric9").next(".error:not(input)").text(), "email" ); +}); test("read messages from metadata, with meta option specified, but no metadata in there", function() { - var form = $("#testForm1clean") + var form = $("#testForm1clean"); form.validate({ meta: "validate", rules: { - firstname: "required" + firstnamec: "required" } }); ok(!form.valid(), "not valid"); diff --git a/wqflask/wqflask/static/new/packages/ValidationPlugin/test/methods.js b/wqflask/wqflask/static/new/packages/ValidationPlugin/test/methods.js index 61643c99..9b95ca79 100755..100644 --- a/wqflask/wqflask/static/new/packages/ValidationPlugin/test/methods.js +++ b/wqflask/wqflask/static/new/packages/ValidationPlugin/test/methods.js @@ -1,9 +1,10 @@ (function($) { function methodTest( methodName ) { - var v = jQuery("#form").validate(); - var method = $.validator.methods[methodName]; - var element = $("#firstname")[0]; + var v = jQuery("#form").validate(), + method = $.validator.methods[methodName], + element = $("#firstname")[0]; + return function(value, param) { element.value = value; return method.call( v, value, element, param ); @@ -36,8 +37,12 @@ test("url", function() { ok( method( "ftp://bassistance.de/jquery/plugin.php?bla=blu" ), "Valid url" ); ok( method( "http://www.føtex.dk/" ), "Valid url, danish unicode characters" ); ok( method( "http://bösendorfer.de/" ), "Valid url, german unicode characters" ); - ok( method( "http://192.168.8.5" ), "Valid IP Address" ) - ok(!method( "http://192.168.8." ), "Invalid IP Address" ) + ok( method( "http://142.42.1.1" ), "Valid IP Address" ); + ok( method( "http://pro.photography" ), "Valid long TLD" ); + ok( method( "//code.jquery.com/jquery-1.11.3.min.js" ), "Valid protocol-relative url" ); + ok( method( "//142.42.1.1" ), "Valid protocol-relative IP Address" ); + ok(!method( "htp://code.jquery.com/jquery-1.11.3.min.js" ), "Invalid protocol" ); + ok(!method( "http://192.168.8." ), "Invalid IP Address" ); ok(!method( "http://bassistance" ), "Invalid url" ); // valid ok(!method( "http://bassistance." ), "Invalid url" ); // valid ok(!method( "http://bassistance,de" ), "Invalid url" ); @@ -53,8 +58,8 @@ test("url2 (tld optional)", function() { ok( method( "ftp://bassistance.de/jquery/plugin.php?bla=blu" ), "Valid url" ); ok( method( "http://www.føtex.dk/" ), "Valid url, danish unicode characters" ); ok( method( "http://bösendorfer.de/" ), "Valid url, german unicode characters" ); - ok( method( "http://192.168.8.5" ), "Valid IP Address" ) - ok(!method( "http://192.168.8." ), "Invalid IP Address" ) + ok( method( "http://192.168.8.5" ), "Valid IP Address" ); + ok(!method( "http://192.168.8." ), "Invalid IP Address" ); ok( method( "http://bassistance" ), "Invalid url" ); ok( method( "http://bassistance." ), "Invalid url" ); ok(!method( "http://bassistance,de" ), "Invalid url" ); @@ -70,46 +75,21 @@ test("email", function() { ok( method( "bart+bart@tokbox.com" ), "Valid email" ); ok( method( "bart+bart@tokbox.travel" ), "Valid email" ); ok( method( "n@d.tld" ), "Valid email" ); - ok( method( "ole@føtex.dk"), "Valid email" ); - ok( method( "jörn@bassistance.de"), "Valid email" ); ok( method( "bla.blu@g.mail.com"), "Valid email" ); - ok( method( "\"Scott Gonzalez\"@example.com" ), "Valid email" ); - ok( method( "\"Scott González\"@example.com" ), "Valid email" ); - ok( method( "\"name.\"@domain.tld" ), "Valid email" ); // valid without top label - ok( method( "\"name,\"@domain.tld" ), "Valid email" ); // valid without top label - ok( method( "\"name;\"@domain.tld" ), "Valid email" ); // valid without top label + ok( method( "name@domain" ), "Valid email" ); + ok( method( "name.@domain.tld" ), "Valid email" ); + ok( method( "name@website.a" ), "Valid email" ); + ok( method( "name@pro.photography" ), "Valid email" ); + ok(!method( "ole@føtex.dk"), "Invalid email" ); + ok(!method( "jörn@bassistance.de"), "Invalid email" ); ok(!method( "name" ), "Invalid email" ); + ok(!method( "test@test-.com" ), "Invalid email" ); ok(!method( "name@" ), "Invalid email" ); - ok(!method( "name@domain" ), "Invalid email" ); - ok(!method( "name.@domain.tld" ), "Invalid email" ); ok(!method( "name,@domain.tld" ), "Invalid email" ); ok(!method( "name;@domain.tld" ), "Invalid email" ); ok(!method( "name;@domain.tld." ), "Invalid email" ); }); -test("email2 (tld optional)", function() { - var method = methodTest("email2"); - ok( method( "name@domain.tld" ), "Valid email" ); - ok( method( "name@domain.tl" ), "Valid email" ); - ok( method( "bart+bart@tokbox.com" ), "Valid email" ); - ok( method( "bart+bart@tokbox.travel" ), "Valid email" ); - ok( method( "n@d.tld" ), "Valid email" ); - ok( method( "ole@føtex.dk"), "Valid email" ); - ok( method( "jörn@bassistance.de"), "Valid email" ); - ok( method( "bla.blu@g.mail.com"), "Valid email" ); - ok( method( "\"Scott Gonzalez\"@example.com" ), "Valid email" ); - ok( method( "\"Scott González\"@example.com" ), "Valid email" ); - ok( method( "\"name.\"@domain.tld" ), "Valid email" ); // valid without top label - ok( method( "\"name,\"@domain.tld" ), "Valid email" ); // valid without top label - ok( method( "\"name;\"@domain.tld" ), "Valid email" ); // valid without top label - ok(!method( "name" ), "Invalid email" ); - ok(!method( "name@" ), "Invalid email" ); - ok( method( "name@domain" ), "Invalid email" ); - ok(!method( "name.@domain.tld" ), "Invalid email" ); - ok(!method( "name,@domain.tld" ), "Invalid email" ); - ok(!method( "name;@domain.tld" ), "Invalid email" ); -}); - test("number", function() { var method = methodTest("number"); ok( method( "123" ), "Valid number" ); @@ -118,6 +98,7 @@ test("number", function() { ok( method( "-123,000" ), "Valid number" ); ok( method( "123,000.00" ), "Valid number" ); ok( method( "-123,000.00" ), "Valid number" ); + ok(!method( "-" ), "Invalid number" ); ok(!method( "123.000,00" ), "Invalid number" ); ok(!method( "123.0.0,0" ), "Invalid number" ); ok(!method( "x123" ), "Invalid number" ); @@ -176,11 +157,20 @@ test("date", function() { test("dateISO", function() { var method = methodTest("dateISO"); ok( method( "1990-06-06" ), "Valid date" ); + ok( method( "1990-01-01" ), "Valid date" ); + ok( method( "1990-01-31" ), "Valid date" ); + ok( method( "1990-12-01" ), "Valid date" ); + ok( method( "1990-12-31" ), "Valid date" ); ok( method( "1990/06/06" ), "Valid date" ); ok( method( "1990-6-6" ), "Valid date" ); ok( method( "1990/6/6" ), "Valid date" ); ok(!method( "1990-106-06" ), "Invalid date" ); ok(!method( "190-06-06" ), "Invalid date" ); + ok(!method( "1990-00-06" ), "Invalid date" ); + ok(!method( "1990-13-01" ), "Invalid date" ); + ok(!method( "1990-01-00" ), "Invalid date" ); + ok(!method( "1990-01-32" ), "Invalid date" ); + ok(!method( "1990-13-32" ), "Invalid date" ); }); /* disabled for now, need to figure out how to test localized methods @@ -198,30 +188,30 @@ test("dateDE", function() { test("required", function() { var v = jQuery("#form").validate(), method = $.validator.methods.required, - e = $('#text1, #text1b, #hidden2, #select1, #select2'); + e = $("#text1, #text1b, #hidden2, #select1, #select2"); ok( method.call( v, e[0].value, e[0]), "Valid text input" ); ok(!method.call( v, e[1].value, e[1]), "Invalid text input" ); - ok(!method.call( v, e[1].value, e[2]), "Invalid text input" ); + ok(!method.call( v, e[2].value, e[2]), "Invalid text input" ); - ok(!method.call( v, e[2].value, e[3]), "Invalid select" ); - ok( method.call( v, e[3].value, e[4]), "Valid select" ); + ok(!method.call( v, e[3].value, e[3]), "Invalid select" ); + ok( method.call( v, e[4].value, e[4]), "Valid select" ); - e = $('#area1, #area2, #pw1, #pw2'); + e = $("#area1, #area2, #pw1, #pw2"); ok( method.call( v, e[0].value, e[0]), "Valid textarea" ); ok(!method.call( v, e[1].value, e[1]), "Invalid textarea" ); ok( method.call( v, e[2].value, e[2]), "Valid password input" ); ok(!method.call( v, e[3].value, e[3]), "Invalid password input" ); - e = $('#radio1, #radio2, #radio3'); + e = $("#radio1, #radio2, #radio3"); ok(!method.call( v, e[0].value, e[0]), "Invalid radio" ); ok( method.call( v, e[1].value, e[1]), "Valid radio" ); ok( method.call( v, e[2].value, e[2]), "Valid radio" ); - e = $('#check1, #check2'); + e = $("#check1, #check2"); ok( method.call( v, e[0].value, e[0]), "Valid checkbox" ); ok(!method.call( v, e[1].value, e[1]), "Invalid checkbox" ); - e = $('#select1, #select2, #select3, #select4'); + e = $("#select1, #select2, #select3, #select4"); ok(!method.call( v, e[0].value, e[0]), "Invalid select" ); ok( method.call( v, e[1].value, e[1]), "Valid select" ); ok( method.call( v, e[2].value, e[2]), "Valid select" ); @@ -231,37 +221,37 @@ test("required", function() { test("required with dependencies", function() { var v = jQuery("#form").validate(), method = $.validator.methods.required, - e = $('#hidden2, #select1, #area2, #radio1, #check2'); - ok( method.call( v, e[0].value, e[0], "asffsaa"), "Valid text input due to depencie not met" ); - ok(!method.call( v, e[0].value, e[0], "input"), "Invalid text input" ); - ok( method.call( v, e[0].value, e[0], function() { return false; }), "Valid text input due to depencie not met" ); + e = $("#hidden2, #select1, #area2, #radio1, #check2"); + ok( method.call( v, e[0].value, e[0], "asffsaa" ), "Valid text input due to dependency not met" ); + ok(!method.call( v, e[0].value, e[0], "input" ), "Invalid text input" ); + ok( method.call( v, e[0].value, e[0], function() { return false; }), "Valid text input due to dependency not met" ); ok(!method.call( v, e[0].value, e[0], function() { return true; }), "Invalid text input" ); - ok( method.call( v, e[1].value, e[1], "asfsfa"), "Valid select due to dependency not met" ); - ok(!method.call( v, e[1].value, e[1], "input"), "Invalid select" ); - ok( method.call( v, e[2].value, e[2], "asfsafsfa"), "Valid textarea due to dependency not met" ); - ok(!method.call( v, e[2].value, e[2], "input"), "Invalid textarea" ); - ok( method.call( v, e[3].value, e[3], "asfsafsfa"), "Valid radio due to dependency not met" ); - ok(!method.call( v, e[3].value, e[3], "input"), "Invalid radio" ); - ok( method.call( v, e[4].value, e[4], "asfsafsfa"), "Valid checkbox due to dependency not met" ); - ok(!method.call( v, e[4].value, e[4], "input"), "Invalid checkbox" ); + ok( method.call( v, e[1].value, e[1], "asfsfa" ), "Valid select due to dependency not met" ); + ok(!method.call( v, e[1].value, e[1], "input" ), "Invalid select" ); + ok( method.call( v, e[2].value, e[2], "asfsafsfa" ), "Valid textarea due to dependency not met" ); + ok(!method.call( v, e[2].value, e[2], "input" ), "Invalid textarea" ); + ok( method.call( v, e[3].value, e[3], "asfsafsfa" ), "Valid radio due to dependency not met" ); + ok(!method.call( v, e[3].value, e[3], "input" ), "Invalid radio" ); + ok( method.call( v, e[4].value, e[4], "asfsafsfa" ), "Valid checkbox due to dependency not met" ); + ok(!method.call( v, e[4].value, e[4], "input" ), "Invalid checkbox" ); }); test("minlength", function() { var v = jQuery("#form").validate(), method = $.validator.methods.minlength, param = 2, - e = $('#text1, #text1c, #text2, #text3'); + e = $("#text1, #text1c, #text2, #text3"); ok( method.call( v, e[0].value, e[0], param), "Valid text input" ); - ok(!method.call( v, e[1].value, e[1], param), "Invalid text input" ); + ok( method.call( v, e[1].value, e[1], param), "Valid text input" ); ok(!method.call( v, e[2].value, e[2], param), "Invalid text input" ); ok( method.call( v, e[3].value, e[3], param), "Valid text input" ); - e = $('#check1, #check2, #check3'); + e = $("#check1, #check2, #check3"); ok(!method.call( v, e[0].value, e[0], param), "Valid checkbox" ); ok( method.call( v, e[1].value, e[1], param), "Valid checkbox" ); ok( method.call( v, e[2].value, e[2], param), "Invalid checkbox" ); - e = $('#select1, #select2, #select3, #select4, #select5'); + e = $("#select1, #select2, #select3, #select4, #select5"); ok(method.call( v, e[0].value, e[0], param), "Valid select " + e[0].id ); ok(!method.call( v, e[1].value, e[1], param), "Invalid select " + e[1].id ); ok( method.call( v, e[2].value, e[2], param), "Valid select " + e[2].id ); @@ -270,20 +260,21 @@ test("minlength", function() { }); test("maxlength", function() { - var v = jQuery("#form").validate(); - var method = $.validator.methods.maxlength, + var v = jQuery("#form").validate(), + method = $.validator.methods.maxlength, param = 4, - e = $('#text1, #text2, #text3'); + e = $("#text1, #text2, #text3"); + ok( method.call( v, e[0].value, e[0], param), "Valid text input" ); ok( method.call( v, e[1].value, e[1], param), "Valid text input" ); ok(!method.call( v, e[2].value, e[2], param), "Invalid text input" ); - e = $('#check1, #check2, #check3'); + e = $("#check1, #check2, #check3"); ok( method.call( v, e[0].value, e[0], param), "Valid checkbox" ); ok( method.call( v, e[1].value, e[1], param), "Invalid checkbox" ); ok(!method.call( v, e[2].value, e[2], param), "Invalid checkbox" ); - e = $('#select1, #select2, #select3, #select4'); + e = $("#select1, #select2, #select3, #select4"); ok( method.call( v, e[0].value, e[0], param), "Valid select" ); ok( method.call( v, e[1].value, e[1], param), "Valid select" ); ok( method.call( v, e[2].value, e[2], param), "Valid select" ); @@ -291,104 +282,111 @@ test("maxlength", function() { }); test("rangelength", function() { - var v = jQuery("#form").validate(); - var method = $.validator.methods.rangelength, - param = [2, 4], - e = $('#text1, #text2, #text3'); + var v = jQuery("#form").validate(), + method = $.validator.methods.rangelength, + param = [ 2, 4 ], + e = $("#text1, #text2, #text3"); + ok( method.call( v, e[0].value, e[0], param), "Valid text input" ); ok(!method.call( v, e[1].value, e[1], param), "Invalid text input" ); ok(!method.call( v, e[2].value, e[2], param), "Invalid text input" ); }); test("min", function() { - var v = jQuery("#form").validate(); - var method = $.validator.methods.min, + var v = jQuery("#form").validate(), + method = $.validator.methods.min, param = 8, - e = $('#value1, #value2, #value3'); + e = $("#value1, #value2, #value3"); + ok(!method.call( v, e[0].value, e[0], param), "Invalid text input" ); ok( method.call( v, e[1].value, e[1], param), "Valid text input" ); ok( method.call( v, e[2].value, e[2], param), "Valid text input" ); }); test("max", function() { - var v = jQuery("#form").validate(); - var method = $.validator.methods.max, + var v = jQuery("#form").validate(), + method = $.validator.methods.max, param = 12, - e = $('#value1, #value2, #value3'); + e = $("#value1, #value2, #value3"); + ok( method.call( v, e[0].value, e[0], param), "Valid text input" ); ok( method.call( v, e[1].value, e[1], param), "Valid text input" ); ok(!method.call( v, e[2].value, e[2], param), "Invalid text input" ); }); test("range", function() { - var v = jQuery("#form").validate(); - var method = $.validator.methods.range, - param = [4,12], - e = $('#value1, #value2, #value3'); + var v = jQuery("#form").validate(), + method = $.validator.methods.range, + param = [ 4, 12 ], + e = $("#value1, #value2, #value3"); + ok(!method.call( v, e[0].value, e[0], param), "Invalid text input" ); ok( method.call( v, e[1].value, e[1], param), "Valid text input" ); ok(!method.call( v, e[2].value, e[2], param), "Invalid text input" ); }); test("equalTo", function() { - var v = jQuery("#form").validate(); - var method = $.validator.methods.equalTo, - e = $('#text1, #text2'); - ok( method.call( v, "Test", e[0], "#text1"), "Text input" ); - ok( method.call( v, "T", e[1], "#text2"), "Another one" ); + var v = jQuery("#form").validate(), + method = $.validator.methods.equalTo, + e = $("#text1, #text2"); + + ok( method.call( v, "Test", e[0], "#text1" ), "Text input" ); + ok( method.call( v, "T", e[1], "#text2" ), "Another one" ); }); test("creditcard", function() { var method = methodTest("creditcard"); - ok( method( "446-667-651" ), "Valid creditcard number" ); - ok( method( "446 667 651" ), "Valid creditcard number" ); - ok( !method( "asdf" ), "Invalid creditcard number" ); + ok( method( "4111-1111-1111-1111" ), "Valid creditcard number" ); + ok( method( "4111 1111 1111 1111" ), "Valid creditcard number" ); + ok(!method( "41111" ), "Invalid creditcard number" ); + ok(!method( "asdf" ), "Invalid creditcard number" ); }); test("extension", function() { - var method = methodTest("extension"); + var method = methodTest("extension"), + v; ok( method( "picture.gif" ), "Valid default accept type" ); ok( method( "picture.jpg" ), "Valid default accept type" ); ok( method( "picture.jpeg" ), "Valid default accept type" ); ok( method( "picture.png" ), "Valid default accept type" ); - ok( !method( "picture.pgn" ), "Invalid default accept type" ); + ok(!method( "picture.pgn" ), "Invalid default accept type" ); - var v = jQuery("#form").validate(), - method = function(value, param) { - return $.validator.methods.extension.call(v, value, $('#text1')[0], param); - }; - ok( method( "picture.doc", "doc"), "Valid custom accept type" ); - ok( method( "picture.pdf", "doc|pdf"), "Valid custom accept type" ); - ok( method( "picture.pdf", "pdf|doc"), "Valid custom accept type" ); - ok( !method( "picture.pdf", "doc"), "Invalid custom accept type" ); - ok( !method( "picture.doc", "pdf"), "Invalid custom accept type" ); - - ok( method( "picture.pdf", "doc,pdf"), "Valid custom accept type, comma seperated" ); - ok( method( "picture.pdf", "pdf,doc"), "Valid custom accept type, comma seperated" ); - ok( !method( "picture.pdf", "gop,top"), "Invalid custom accept type, comma seperated" ); + v = jQuery("#form").validate(); + method = function(value, param) { + return $.validator.methods.extension.call(v, value, $("#text1")[0], param); + }; + ok( method( "picture.doc", "doc" ), "Valid custom accept type" ); + ok( method( "picture.pdf", "doc|pdf" ), "Valid custom accept type" ); + ok( method( "picture.pdf", "pdf|doc" ), "Valid custom accept type" ); + ok(!method( "picture.pdf", "doc" ), "Invalid custom accept type" ); + ok(!method( "picture.doc", "pdf" ), "Invalid custom accept type" ); + + ok( method( "picture.pdf", "doc,pdf" ), "Valid custom accept type, comma separated" ); + ok( method( "picture.pdf", "pdf,doc" ), "Valid custom accept type, comma separated" ); + ok(!method( "picture.pdf", "gop,top" ), "Invalid custom accept type, comma separated" ); }); -test("remote", function() { +asyncTest("remote", function() { expect(7); - stop(); - var e = $("#username"); - var v = $("#userForm").validate({ - rules: { - username: { - required: true, - remote: "users.php" - } - }, - messages: { - username: { - required: "Please", - remote: jQuery.validator.format("{0} in use") + var e = $("#username"), + v = $("#userForm").validate({ + rules: { + username: { + required: true, + remote: "users.php" + } + }, + messages: { + username: { + required: "Please", + remote: jQuery.validator.format("{0} in use") + } + }, + submitHandler: function() { + ok( false, "submitHandler may never be called when validating only elements"); } - }, - submitHandler: function() { - ok( false, "submitHandler may never be called when validating only elements"); - } - }); + }); + $(document).ajaxStop(function() { $(document).unbind("ajaxStop"); equal( 1, v.size(), "There must be one error" ); @@ -408,10 +406,9 @@ test("remote", function() { strictEqual( v.element(e), true, "still invalid, because remote validation must block until it returns; dependency-mismatch considered as valid though" ); }); -test("remote, customized ajax options", function() { +asyncTest("remote, customized ajax options", function() { expect(2); - stop(); - var v = $("#userForm").validate({ + $("#userForm").validate({ rules: { username: { required: true, @@ -438,210 +435,443 @@ test("remote, customized ajax options", function() { $("#userForm").valid(); }); - -test("remote extensions", function() { +asyncTest("remote extensions", function() { expect(5); - stop(); - var e = $("#username"); - var v = $("#userForm").validate({ - rules: { - username: { - required: true, - remote: "users2.php" - } - }, - messages: { - username: { - required: "Please" - } - }, - submitHandler: function() { - ok( false, "submitHandler may never be called when validating only elements"); - } - }); - $(document).ajaxStop(function() { - $(document).unbind("ajaxStop"); - equal( 1, v.size(), "There must be one error" ); - equal( v.errorList[0].message, "asdf is already taken, please try something else" ); - v.element(e); - equal( v.errorList[0].message, "asdf is already taken, please try something else", "message doesn't change on revalidation" ); - start(); - }); - strictEqual( v.element(e), false, "invalid element, nothing entered yet" ); - e.val("asdf"); - strictEqual( v.element(e), true, "still invalid, because remote validation must block until it returns; dependency-mismatch considered as valid though" ); -}); - -asyncTest("remote radio correct value sent", function() { - expect(1); - var e = $("#testForm10Radio2"); - e.attr('checked', 'checked'); - var v = $("#testForm10").validate({ - rules: { - testForm10Radio: { - required: true, - remote: { - url: "echo.php", - dataType: "json", - success: function(data) { - equal( data['testForm10Radio'], '2', ' correct radio value sent' ); - start(); - } + var e = $("#username"), + v = $("#userForm").validate({ + rules: { + username: { + required: true, + remote: "users2.php" } }, - } - }); - - v.element(e); -}); - -asyncTest("remote reset clear old value", function() { - expect(1); - - var e = $("#username"); - var v = $("#userForm").validate({ - rules: { - username: { - required: true, - remote: { - url: "echo.php", - dataFilter: function(data) { - var json = JSON.parse(data); - if(json.username == 'asdf') { - return "\"asdf is already taken\""; - } - return "\"" + true + "\""; - } + messages: { + username: { + required: "Please" } + }, + submitHandler: function() { + ok( false, "submitHandler may never be called when validating only elements"); } - } - }); - $(document).ajaxStop(function() { - var waitTimeout; - - $(document).unbind("ajaxStop"); - - - $(document).ajaxStop(function() { - clearTimeout(waitTimeout); - ok( true, "Remote request sent to server" ); - start(); }); - - v.resetForm(); - e.val("asdf"); - waitTimeout = setTimeout(function() { - ok( false, "Remote server did not get request"); - start(); - }, 200); - v.element(e); + $(document).ajaxStop(function() { + $(document).unbind("ajaxStop"); + if ( v.size() !== 0 ) { + ok( "There must be one error" ); + equal( v.errorList[0].message, "asdf is already taken, please try something else" ); + v.element(e); + equal( v.errorList[0].message, "asdf is already taken, please try something else", "message doesn't change on revalidation" ); + } + start(); }); + strictEqual( v.element(e), false, "invalid element, nothing entered yet" ); e.val("asdf"); - v.element(e); + strictEqual( v.element(e), true, "still invalid, because remote validation must block until it returns; dependency-mismatch considered as valid though" ); }); module("additional methods"); test("phone (us)", function() { var method = methodTest("phoneUS"); - ok( method( "1(212)-999-2345" ), "Valid us phone number" ); - ok( method( "212 999 2344" ), "Valid us phone number" ); - ok( method( "212-999-0983" ), "Valid us phone number" ); - ok(!method( "111-123-5434" ), "Invalid us phone number" ); - ok(!method( "212 123 4567" ), "Invalid us phone number" ); + ok( method( "1(212)-999-2345" ), "Valid US phone number" ); + ok( method( "212 999 2344" ), "Valid US phone number" ); + ok( method( "212-999-0983" ), "Valid US phone number" ); + ok(!method( "111-123-5434" ), "Invalid US phone number. Area Code cannot start with 1" ); + ok(!method( "212 123 4567" ), "Invalid US phone number. NXX cannot start with 1" ); + ok(!method( "234-911-5678" ), "Invalid US phone number, because the exchange code cannot be in the form N11" ); + ok(!method( "911-333-5678" ), "Invalid US phone number, because the area code cannot be in the form N11" ); + ok(method( "234-912-5678" ), "Valid US phone number" ); +}); + +test("phoneUK", function() { + var method = methodTest("phoneUK"); + ok( method( "0117 333 5555" ), "Valid UK Phone Number" ); + ok( method( "0121 555 5555" ), "Valid UK Phone Number" ); + ok( method( "01633 555555" ), "Valid UK Phone Number" ); + ok( method( "01298 28555" ), "Valid UK Phone Number" ); + ok( method( "015395 55555" ), "Valid UK Phone Number" ); + ok( method( "016977 3999" ), "Valid UK Phone Number" ); + ok( method( "020 3000 5555" ), "Valid UK Phone Number" ); + ok( method( "024 7500 5555" ), "Valid UK Phone Number" ); + ok( method( "0333 555 5555" ), "Valid UK Phone Number" ); + ok( method( "0500 555555" ), "Valid UK Phone Number" ); + ok( method( "055 3555 5555" ), "Valid UK Phone Number" ); + ok( method( "07122 555555" ), "Valid UK Phone Number" ); + ok( method( "07222 555555" ), "Valid UK Phone Number" ); + ok( method( "07322 555555" ), "Valid UK Phone Number" ); + ok( method( "0800 555 5555" ), "Valid UK Phone Number" ); + ok( method( "0800 355555" ), "Valid UK Phone Number" ); + ok( method( "0843 555 5555" ), "Valid UK Phone Number" ); + ok( method( "0872 555 5555" ), "Valid UK Phone Number" ); + ok( method( "0903 555 5555" ), "Valid UK Phone Number" ); + ok( method( "0983 555 5555" ), "Valid UK Phone Number" ); + ok( method( "(07122) 555555" ), "Valid UK Phone Number" ); + ok( method( "(07222) 555555" ), "Valid UK Phone Number" ); + ok( method( "(07322) 555555" ), "Valid UK Phone Number" ); + ok( method( "+44 7122 555 555" ), "Valid UK Phone Number" ); + ok( method( "+44 7222 555 555" ), "Valid UK Phone Number" ); + ok( method( "+44 7322 555 555" ), "Valid UK Phone Number" ); + ok(!method( "7222 555555" ), "Invalid UK Phone Number" ); + ok(!method( "+44 07222 555555" ), "Invalid UK Phone Number" ); }); test("mobileUK", function() { var method = methodTest("mobileUK"); + ok( method( "07134234323" ), "Valid UK Mobile Number" ); + ok( method( "07334234323" ), "Valid UK Mobile Number" ); + ok( method( "07624234323" ), "Valid UK Mobile Number" ); ok( method( "07734234323" ), "Valid UK Mobile Number" ); + ok( method( "+447134234323" ), "Valid UK Mobile Number" ); + ok( method( "+447334234323" ), "Valid UK Mobile Number" ); + ok( method( "+447624234323" ), "Valid UK Mobile Number" ); ok( method( "+447734234323" ), "Valid UK Mobile Number" ); - ok( !method( "07034234323" ), "Invalid UK Mobile Number" ); - ok( !method( "0753423432" ), "Invalid UK Mobile Number" ); - ok( !method( "07604234323" ), "Invalid UK Mobile Number" ); - ok( !method( "077342343234" ), "Invalid UK Mobile Number" ); - ok( !method( "044342343234" ), "Invalid UK Mobile Number" ); - ok( !method( "+44753423432" ), "Invalid UK Mobile Number" ); - ok( !method( "+447604234323" ), "Invalid UK Mobile Number" ); - ok( !method( "+4477342343234" ), "Invalid UK Mobile Number" ); - ok( !method( "+4444342343234" ), "Invalid UK Mobile Number" ); + ok(!method( "07034234323" ), "Invalid UK Mobile Number" ); + ok(!method( "0753423432" ), "Invalid UK Mobile Number" ); + ok(!method( "07604234323" ), "Invalid UK Mobile Number" ); + ok(!method( "077342343234" ), "Invalid UK Mobile Number" ); + ok(!method( "044342343234" ), "Invalid UK Mobile Number" ); + ok(!method( "+44753423432" ), "Invalid UK Mobile Number" ); + ok(!method( "+447604234323" ), "Invalid UK Mobile Number" ); + ok(!method( "+4477342343234" ), "Invalid UK Mobile Number" ); + ok(!method( "+4444342343234" ), "Invalid UK Mobile Number" ); }); test("dateITA", function() { var method = methodTest("dateITA"); ok( method( "01/01/1900" ), "Valid date ITA" ); + ok( method( "17/10/2010" ), "Valid date ITA" ); ok(!method( "01/13/1990" ), "Invalid date ITA" ); ok(!method( "01.01.1900" ), "Invalid date ITA" ); + ok(!method( "01/01/199" ), "Invalid date ITA" ); +}); + +test("dateFA", function() { + var method = methodTest("dateFA"); + + ok( method( "1342/12/29" ), "Valid date FA" ); + ok( method( "1342/12/30" ), "Valid date FA" ); + ok( method( "1361/6/31" ), "Valid date FA" ); + ok( method( "1321/11/30" ), "Valid date FA" ); + ok( method( "1361/1/1" ), "Valid date FA" ); + ok( method( "1020/3/3" ), "Valid date FA" ); + ok( method( "1020/03/3" ), "Valid date FA" ); + ok( method( "1020/3/03" ), "Valid date FA" ); + ok( method( "1020/03/03" ), "Valid date FA" ); + ok( method( "1001/7/30" ), "Valid date FA" ); + + ok(!method( "1000/1/32" ), "Invalid date FA" ); + ok(!method( "1323/12/31" ), "Invalid date FA" ); + ok(!method( "1361/0/11" ), "Invalid date FA" ); + ok(!method( "63/4/4" ), "Invalid date FA" ); + ok(!method( "15/6/1361" ), "Invalid date FA" ); +}); + +test("iban", function() { + var method = methodTest("iban"); + ok( method( "NL20INGB0001234567"), "Valid IBAN"); + ok( method( "DE68 2105 0170 0012 3456 78"), "Valid IBAN"); + ok( method( "NL20 INGB0001234567"), "Valid IBAN: invalid spacing"); + ok( method( "NL20 INGB 00 0123 4567"), "Valid IBAN: invalid spacing"); + ok( method( "XX40INGB000123456712341234"), "Valid (more or less) IBAN: unknown country, but checksum OK"); + + ok(!method( "NL20INGB000123456"), "Invalid IBAN: too short"); + ok(!method( "NL20INGB00012345678"), "Invalid IBAN: too long"); + ok(!method( "NL20INGB0001234566"), "Invalid IBAN: checksum incorrect"); + ok(!method( "DE68 2105 0170 0012 3456 7"), "Invalid IBAN: too short"); + ok(!method( "DE68 2105 0170 0012 3456 789"), "Invalid IBAN: too long"); + ok(!method( "DE68 2105 0170 0012 3456 79"), "Invalid IBAN: checksum incorrect"); + + ok(!method( "NL54INGB00012345671234"), "Invalid IBAN too long, BUT CORRECT CHECKSUM"); + ok(!method( "XX00INGB000123456712341234"), "Invalid IBAN: unknown country and checksum incorrect"); + + // sample IBANs for different countries + ok( method( "AL47 2121 1009 0000 0002 3569 8741"), "Valid IBAN - AL"); + ok( method( "AD12 0001 2030 2003 5910 0100"), "Valid IBAN - AD"); + ok( method( "AT61 1904 3002 3457 3201"), "Valid IBAN - AT"); + ok( method( "AZ21 NABZ 0000 0000 1370 1000 1944"), "Valid IBAN - AZ"); + ok( method( "BH67 BMAG 0000 1299 1234 56"), "Valid IBAN - BH"); + ok( method( "BE62 5100 0754 7061"), "Valid IBAN - BE"); + ok( method( "BA39 1290 0794 0102 8494"), "Valid IBAN - BA"); + ok( method( "BG80 BNBG 9661 1020 3456 78"), "Valid IBAN - BG"); + ok( method( "HR12 1001 0051 8630 0016 0"), "Valid IBAN - HR"); + ok( method( "CH93 0076 2011 6238 5295 7"), "Valid IBAN - CH"); + ok( method( "CY17 0020 0128 0000 0012 0052 7600"), "Valid IBAN - CY"); + ok( method( "CZ65 0800 0000 1920 0014 5399"), "Valid IBAN - CZ"); + ok( method( "DK50 0040 0440 1162 43"), "Valid IBAN - DK"); + ok( method( "EE38 2200 2210 2014 5685"), "Valid IBAN - EE"); + ok( method( "FO97 5432 0388 8999 44"), "Valid IBAN - FO"); + ok( method( "FI21 1234 5600 0007 85"), "Valid IBAN - FI"); + ok( method( "FR14 2004 1010 0505 0001 3M02 606"), "Valid IBAN - FR"); + ok( method( "GE29 NB00 0000 0101 9049 17"), "Valid IBAN - GE"); + ok( method( "DE89 3704 0044 0532 0130 00"), "Valid IBAN - DE"); + ok( method( "GI75 NWBK 0000 0000 7099 453"), "Valid IBAN - GI"); + ok( method( "GR16 0110 1250 0000 0001 2300 695"), "Valid IBAN - GR"); + ok( method( "GL56 0444 9876 5432 10"), "Valid IBAN - GL"); + ok( method( "HU42 1177 3016 1111 1018 0000 0000"), "Valid IBAN - HU"); + ok( method( "IS14 0159 2600 7654 5510 7303 39"), "Valid IBAN - IS"); + ok( method( "IE29 AIBK 9311 5212 3456 78"), "Valid IBAN - IE"); + ok( method( "IL62 0108 0000 0009 9999 999"), "Valid IBAN - IL"); + ok( method( "IT40 S054 2811 1010 0000 0123 456"), "Valid IBAN - IT"); + ok( method( "LV80 BANK 0000 4351 9500 1"), "Valid IBAN - LV"); + ok( method( "LB62 0999 0000 0001 0019 0122 9114"), "Valid IBAN - LB"); + ok( method( "LI21 0881 0000 2324 013A A"), "Valid IBAN - LI"); + ok( method( "LT12 1000 0111 0100 1000"), "Valid IBAN - LT"); + ok( method( "LU28 0019 4006 4475 0000"), "Valid IBAN - LU"); + ok( method( "MK07 2501 2000 0058 984"), "Valid IBAN - MK"); + ok( method( "MT84 MALT 0110 0001 2345 MTLC AST0 01S"), "Valid IBAN - MT"); + ok( method( "MU17 BOMM 0101 1010 3030 0200 000M UR"), "Valid IBAN - MU"); + ok( method( "MD24 AG00 0225 1000 1310 4168"), "Valid IBAN - MD"); + ok( method( "MC93 2005 2222 1001 1223 3M44 555"), "Valid IBAN - MC"); + ok( method( "ME25 5050 0001 2345 6789 51"), "Valid IBAN - ME"); + ok( method( "NL39 RABO 0300 0652 64"), "Valid IBAN - NL"); + ok( method( "NO93 8601 1117 947"), "Valid IBAN - NO"); + ok( method( "PK36 SCBL 0000 0011 2345 6702"), "Valid IBAN - PK"); + ok( method( "PL60 1020 1026 0000 0422 7020 1111"), "Valid IBAN - PL"); + ok( method( "PT50 0002 0123 1234 5678 9015 4"), "Valid IBAN - PT"); + ok( method( "RO49 AAAA 1B31 0075 9384 0000"), "Valid IBAN - RO"); + ok( method( "SM86 U032 2509 8000 0000 0270 100"), "Valid IBAN - SM"); + ok( method( "SA03 8000 0000 6080 1016 7519"), "Valid IBAN - SA"); + ok( method( "RS35 2600 0560 1001 6113 79"), "Valid IBAN - RS"); + ok( method( "SK31 1200 0000 1987 4263 7541"), "Valid IBAN - SK"); + ok( method( "SI56 1910 0000 0123 438"), "Valid IBAN - SI"); + ok( method( "ES80 2310 0001 1800 0001 2345"), "Valid IBAN - ES"); + ok( method( "SE35 5000 0000 0549 1000 0003"), "Valid IBAN - SE"); + ok( method( "CH93 0076 2011 6238 5295 7"), "Valid IBAN - CH"); + ok( method( "TN59 1000 6035 1835 9847 8831"), "Valid IBAN - TN"); + ok( method( "TR33 0006 1005 1978 6457 8413 26"), "Valid IBAN - TR"); + ok( method( "AE07 0331 2345 6789 0123 456"), "Valid IBAN - AE"); + ok( method( "GB29 NWBK 6016 1331 9268 19"), "Valid IBAN - GB"); +}); + +/** + * BIC tests (For BIC definition take a look on the implementation itself) + */ +test("bic", function() { + var method = methodTest( "bic" ); + + ok( !method( "PBNKDEF" ), "Invalid BIC: too short" ); + ok( !method( "DEUTDEFFA1" ), "Invalid BIC: disallowed length" ); + ok( !method( "PBNKDEFFXXX1" ), "Invalid BIC: too long" ); + ok( !method( "1BNKDEFF" ), "Invalid BIC: invalid digit" ); + ok( !method( "PBNKDE1F" ), "Invalid BIC: invalid digit" ); + ok( !method( "PBNKDEF3" ), "Invalid BIC: invalid digit" ); + ok( !method( "PBNKDEFO" ), "Invalid BIC: invalid char" ); + ok( !method( "INGDDEFFXAA" ), "Invalid BIC: invalid char" ); + ok( !method( "DEUTDEF0" ), "Invalid BIC: invalid digit" ); + + ok( method( "DEUTDEFF" ), "Valid BIC" ); + ok( method( "DEUTDEFFXXX" ), "Valid BIC" ); + ok( method( "PBNKDE2F" ), "Valid BIC" ); + ok( method( "INGDDEFF101" ), "Valid BIC" ); + ok( method( "INGDDEF2134" ), "Valid BIC" ); + ok( method( "INGDDE91XXX" ), "Valid BIC" ); + ok( method( "INGDDEF2" ), "Valid BIC" ); + ok( method( "AAFFFRP1" ), "Valid BIC" ); + ok( method( "DEUTDEFFAB1" ), "Valid BIC" ); + ok( method( "DEUTDEFFAXX" ), "Valid BIC" ); +}); + +test("postcodeUK", function() { + var method = methodTest("postcodeUK"); + ok( method( "AA9A 9AA" ), "Valid postcode" ); + ok( method( "A9A 9AA" ), "Valid postcode" ); + ok( method( "A9 9AA" ), "Valid postcode" ); + ok( method( "A99 9AA" ), "Valid postcode" ); + ok( method( "AA9 9AA" ), "Valid postcode" ); + ok( method( "AA99 9AA" ), "Valid postcode" ); + + // Channel Island + ok(!method( "AAAA 9AA" ), "Invalid postcode" ); + ok(!method( "AA-2640" ), "Invalid postcode" ); + + ok(!method( "AAA AAA" ), "Invalid postcode" ); + ok(!method( "AA AAAA" ), "Invalid postcode" ); + ok(!method( "A AAAA" ), "Invalid postcode" ); + ok(!method( "AAAAA" ), "Invalid postcode" ); + ok(!method( "999 999" ), "Invalid postcode" ); + ok(!method( "99 9999" ), "Invalid postcode" ); + ok(!method( "9 9999" ), "Invalid postcode" ); + ok(!method( "99999" ), "Invalid postcode" ); +}); + +test("dateNL", function() { + var method = methodTest("dateNL"); + ok( method( "01-01-1900" ), "Valid date NL" ); + ok( method( "01.01.1900" ), "Valid date NL" ); + ok( method( "01/01/1900" ), "Valid date NL" ); + ok( method( "01-01-00" ), "Valid date NL" ); + ok( method( "1-01-1900" ), "Valid date NL" ); + ok( method( "10-10-1900" ), "Valid date NL" ); + ok(!method( "0-01-1900" ), "Invalid date NL" ); + ok(!method( "00-01-1900" ), "Invalid date NL" ); + ok(!method( "35-01-1990" ), "Invalid date NL" ); + ok(!method( "01.01.190" ), "Invalid date NL" ); +}); + +test("phoneNL", function() { + var method = methodTest("phoneNL"); + ok( method( "0701234567"), "Valid phone NL"); + ok( method( "0687654321"), "Valid phone NL"); + ok( method( "020-1234567"), "Valid phone NL"); + ok( method( "020 - 12 34 567"), "Valid phone NL"); + ok( method( "010-2345678"), "Valid phone NL"); + ok( method( "+3120-1234567"), "Valid phone NL"); + ok( method( "+31(0)10-2345678"), "Valid phone NL"); + ok(!method( "020-123456"), "Invalid phone NL: too short"); + ok(!method( "020-12345678"), "Invalid phone NL: too long"); + ok(!method( "-0201234567"), "Invalid phone NL"); + ok(!method( "+310201234567"), "Invalid phone NL: no 0 after +31 allowed"); +}); + +test("mobileNL", function() { + var method = methodTest("mobileNL"); + ok( method( "0612345678"), "Valid NL Mobile Number"); + ok( method( "06-12345678"), "Valid NL Mobile Number"); + ok( method( "06-12 345 678"), "Valid NL Mobile Number"); + ok( method( "+316-12345678"), "Valid NL Mobile Number"); + ok( method( "+31(0)6-12345678"), "Valid NL Mobile Number"); + ok(!method( "abcdefghij"), "Invalid NL Mobile Number: text"); + ok(!method( "0123456789"), "Invalid NL Mobile Number: should start with 06"); + ok(!method( "0823456789"), "Invalid NL Mobile Number: should start with 06"); + ok(!method( "06-1234567"), "Invalid NL Mobile Number: too short"); + ok(!method( "06-123456789"), "Invalid NL Mobile Number: too long"); + ok(!method( "-0612345678"), "Invalid NL Mobile Number"); + ok(!method( "+310612345678"), "Invalid NL Mobile Number: no 0 after +31 allowed"); +}); + +test("postalcodeNL", function() { + var method = methodTest("postalcodeNL"); + ok( method( "1234AB"), "Valid NL Postal Code"); + ok( method( "1234ab"), "Valid NL Postal Code"); + ok( method( "1234 AB"), "Valid NL Postal Code"); + ok( method( "6789YZ"), "Valid NL Postal Code"); + ok(!method( "123AA"), "Invalid NL Postal Code: not enough digits"); + ok(!method( "12345ZZ"), "Invalid NL Postal Code: too many digits"); + ok(!method( "1234 AA"), "Invalid NL Postal Code: too many spaces"); + ok(!method( "AA1234"), "Invalid NL Postal Code"); + ok(!method( "1234-AA"), "Invalid NL Postal Code"); +}); + +test("bankaccountNL", function() { + var method = methodTest("bankaccountNL"); + ok( method( "755490975"), "Valid NL bank account"); + ok( method( "75 54 90 975"), "Valid NL bank account"); + ok( method( "123456789"), "Valid NL bank account"); + ok( method( "12 34 56 789"), "Valid NL bank account"); + ok(!method( "12 3456789"), "Valid NL bank account: inconsistent spaces"); + ok(!method( "123 45 67 89"), "Valid NL bank account: incorrect spaces"); + ok(!method( "755490971"), "Invalid NL bank account"); + ok(!method( "755490973"), "Invalid NL bank account"); + ok(!method( "755490979"), "Invalid NL bank account"); + ok(!method( "123456781"), "Invalid NL bank account"); + ok(!method( "123456784"), "Invalid NL bank account"); + ok(!method( "123456788"), "Invalid NL bank account"); +}); + +test("giroaccountNL", function() { + var method = methodTest("giroaccountNL"); + ok( method( "123"), "Valid NL giro account"); + ok( method( "1234567"), "Valid NL giro account"); + ok(!method( "123456788"), "Invalid NL giro account"); +}); + +test("bankorgiroaccountNL", function() { + var method = methodTest("bankorgiroaccountNL"); + ok( method( "123"), "Valid NL giro account"); + ok( method( "1234567"), "Valid NL giro account"); + ok( method( "123456789"), "Valid NL bank account"); + ok(!method( "12345678"), "Invalid NL bank or giro account"); + ok(!method( "123456788"), "Invalid NL bank or giro account"); }); test("time", function() { var method = methodTest("time"); - ok( method("00:00"), "Valid time, lower bound" ); - ok( method("23:59"), "Valid time, upper bound" ); - ok( !method("12"), "Invalid time" ); - ok( !method("00:60"), "Invalid time" ); - ok( !method("24:60"), "Invalid time" ); - ok( !method("24:00"), "Invalid time" ); - ok( !method("29:59"), "Invalid time" ); - ok( !method("30:00"), "Invalid time" ); + ok( method( "00:00" ), "Valid time, lower bound" ); + ok( method( "23:59" ), "Valid time, upper bound" ); + ok( method( "3:59" ), "Valid time, single digit hour" ); + ok(!method( "12" ), "Invalid time" ); + ok(!method( "29:59" ), "Invalid time" ); + ok(!method( "00:60" ), "Invalid time" ); + ok(!method( "24:60" ), "Invalid time" ); + ok(!method( "24:00" ), "Invalid time" ); + ok(!method( "30:00" ), "Invalid time" ); + ok(!method( "29:59" ), "Invalid time" ); + ok(!method( "120:00" ), "Invalid time" ); + ok(!method( "12:001" ), "Invalid time" ); + ok(!method( "12:00a" ), "Invalid time" ); }); test("time12h", function() { var method = methodTest("time12h"); - ok( method("12:00 AM"), "Valid time, lower bound, am" ); - ok( method("11:59 AM"), "Valid time, upper bound, am" ); - ok( method("12:00 PM"), "Valid time, lower bound, pm" ); - ok( method("11:59 PM"), "Valid time, upper bound, pm" ); - ok( method("11:59 am"), "Valid time, also accept lowercase" ); - ok( method("11:59 pm"), "Valid time, also accept lowercase" ); - ok( !method("12:00"), "Invalid time" ); - ok( !method("12:61 am"), "Invalid time" ); - ok( !method("13:00 am"), "Invalid time" ); + ok( method( "12:00 AM" ), "Valid time, lower bound, am" ); + ok( method( "11:59 AM" ), "Valid time, upper bound, am" ); + ok( method( "12:00AM" ), "Valid time, no space, am" ); + ok( method( "12:00PM" ), "Valid time, no space, pm" ); + ok( method( "12:00 PM" ), "Valid time, lower bound, pm" ); + ok( method( "11:59 PM" ), "Valid time, upper bound, pm" ); + ok( method( "11:59 am" ), "Valid time, also accept lowercase" ); + ok( method( "11:59 pm" ), "Valid time, also accept lowercase" ); + ok( method( "1:59 pm" ), "Valid time, single hour, no leading 0" ); + ok( method( "01:59 pm" ), "Valid time, single hour, leading 0" ); + ok(!method( "12:00" ), "Invalid time" ); + ok(!method( "9" ), "Invalid time" ); + ok(!method( "9 am"), "Invalid time" ); + ok(!method( "12:61 am" ), "Invalid time" ); + ok(!method( "13:00 am" ), "Invalid time" ); + ok(!method( "00:00 am" ), "Invalid time" ); }); test("minWords", function() { var method = methodTest("minWords"); - ok( method("hello worlds", 2), "plain text, valid" ); - ok( method("<b>hello</b> world", 2), "html, valid" ); - ok( !method("hello", 2), "plain text, invalid" ); - ok( !method("<b>world</b>", 2), "html, invalid" ); - ok( !method("world <br/>", 2), "html, invalid" ); + ok( method( "hello worlds", 2 ), "plain text, valid" ); + ok( method( "<b>hello</b> world", 2 ), "html, valid" ); + ok(!method( "hello", 2 ), "plain text, invalid" ); + ok(!method( "<b>world</b>", 2 ), "html, invalid" ); + ok(!method( "world <br/>", 2 ), "html, invalid" ); }); test("maxWords", function() { var method = methodTest("maxWords"); - ok( method("hello", 2), "plain text, valid" ); - ok( method("<b>world</b>", 2), "html, valid" ); - ok( method("world <br/>", 2), "html, valid" ); - ok( method("hello worlds", 2), "plain text, valid" ); - ok( method("<b>hello</b> world", 2), "html, valid" ); - ok( !method("hello 123 world", 2), "plain text, invalid" ); - ok( !method("<b>hello</b> 123 world", 2), "html, invalid" ); + ok( method( "hello", 2 ), "plain text, valid" ); + ok( method( "<b>world</b>", 2 ), "html, valid" ); + ok( method( "world <br/>", 2 ), "html, valid" ); + ok( method( "hello worlds", 2 ), "plain text, valid" ); + ok( method( "<b>hello</b> world", 2 ), "html, valid" ); + ok(!method( "hello 123 world", 2 ), "plain text, invalid" ); + ok(!method( "<b>hello</b> 123 world", 2 ), "html, invalid" ); }); test("rangeWords", function() { var method = methodTest("rangeWords"); - ok( method("hello", [0, 2]), "plain text, valid" ); - ok( method("hello worlds", [0, 2]), "plain text, valid" ); - ok( method("<b>hello</b> world", [0, 2]), "html, valid" ); - ok( !method("hello worlds what is up", [0, 2]), "plain text, invalid" ); - ok( !method("<b>Hello</b> <b>world</b> <b>hello</b>", [0, 2]), "html, invalid" ); + ok( method( "hello", [ 0, 2 ] ), "plain text, valid" ); + ok( method( "hello worlds", [ 0, 2 ] ), "plain text, valid" ); + ok( method( "<b>hello</b> world", [ 0, 2 ] ), "html, valid" ); + ok(!method( "hello worlds what is up", [ 0, 2 ] ), "plain text, invalid" ); + ok(!method( "<b>Hello</b> <b>world</b> <b>hello</b>", [ 0, 2 ] ), "html, invalid" ); }); test("pattern", function() { var method = methodTest("pattern"); ok( method( "AR1004", "AR\\d{4}" ), "Correct format for the given RegExp" ); ok( method( "AR1004", /^AR\d{4}$/ ), "Correct format for the given RegExp" ); - ok( !method( "BR1004", /^AR\d{4}$/ ), "Invalid format for the given RegExp" ); + ok(!method( "BR1004", /^AR\d{4}$/ ), "Invalid format for the given RegExp" ); + ok( method( "1ABC", "[0-9][A-Z]{3}" ), "Correct format for the given RegExp" ); + ok(!method( "ABC", "[0-9][A-Z]{3}" ), "Invalid format for the given RegExp" ); + ok(!method( "1ABC DEF", "[0-9][A-Z]{3}" ), "Invalid format for the given RegExp" ); + ok( method( "1ABCdef", "[a-zA-Z0-9]+" ), "Correct format for the given RegExp" ); + ok(!method( "1ABC def", "[a-zA-Z0-9]+" ), "Invalid format for the given RegExp" ); + ok( method( "2014-10-02", "[0-9]{4}-(0[1-9]|1[012])-(0[1-9]|1[0-9]|2[0-9]|3[01])" ), "Correct format for the given RegExp" ); + ok(!method( "02-10-2014", "[0-9]{4}-(0[1-9]|1[012])-(0[1-9]|1[0-9]|2[0-9]|3[01])" ), "Invalid format for the given RegExp" ); }); function testCardTypeByNumber(number, cardname, expected) { $("#cardnumber").val(number); var actual = $("#ccform").valid(); - equal(actual, expected, $.format("Expect card number {0} to validate to {1}, actually validated to ", number, expected)); + equal(actual, expected, $.validator.format("Expect card number {0} to validate to {1}, actually validated to ", number, expected)); } -test('creditcardtypes, all', function() { +test("creditcardtypes, all", function() { $("#ccform").validate({ rules: { cardnumber: { @@ -653,19 +883,18 @@ test('creditcardtypes, all', function() { } }); - testCardTypeByNumber("4111-1111-1111-1111", "VISA", true) - testCardTypeByNumber("5111-1111-1111-1118", "MasterCard", true) - testCardTypeByNumber("6111-1111-1111-1116", "Discover", true) - testCardTypeByNumber("3400-0000-0000-009", "AMEX", true); - - testCardTypeByNumber("4111-1111-1111-1110", "VISA", false) - testCardTypeByNumber("5432-1111-1111-1111", "MasterCard", false) - testCardTypeByNumber("6611-6611-6611-6611", "Discover", false) - testCardTypeByNumber("3777-7777-7777-7777", "AMEX", false) + testCardTypeByNumber( "4111-1111-1111-1111", "VISA", true ); + testCardTypeByNumber( "5111-1111-1111-1118", "MasterCard", true ); + testCardTypeByNumber( "6111-1111-1111-1116", "Discover", true ); + testCardTypeByNumber( "3400-0000-0000-009", "AMEX", true ); + testCardTypeByNumber( "4111-1111-1111-1110", "VISA", false ); + testCardTypeByNumber( "5432-1111-1111-1111", "MasterCard", false ); + testCardTypeByNumber( "6611-6611-6611-6611", "Discover", false ); + testCardTypeByNumber( "3777-7777-7777-7777", "AMEX", false ); }); -test('creditcardtypes, visa', function() { +test("creditcardtypes, visa", function() { $("#ccform").validate({ rules: { cardnumber: { @@ -677,13 +906,13 @@ test('creditcardtypes, visa', function() { } }); - testCardTypeByNumber("4111-1111-1111-1111", "VISA", true) - testCardTypeByNumber("5111-1111-1111-1118", "MasterCard", false) - testCardTypeByNumber("6111-1111-1111-1116", "Discover", false) - testCardTypeByNumber("3400-0000-0000-009", "AMEX", false); + testCardTypeByNumber( "4111-1111-1111-1111", "VISA", true ); + testCardTypeByNumber( "5111-1111-1111-1118", "MasterCard", false ); + testCardTypeByNumber( "6111-1111-1111-1116", "Discover", false ); + testCardTypeByNumber( "3400-0000-0000-009", "AMEX", false ); }); -test('creditcardtypes, mastercard', function() { +test("creditcardtypes, mastercard", function() { $("#ccform").validate({ rules: { cardnumber: { @@ -695,63 +924,302 @@ test('creditcardtypes, mastercard', function() { } }); - testCardTypeByNumber("5111-1111-1111-1118", "MasterCard", true) - testCardTypeByNumber("6111-1111-1111-1116", "Discover", false) - testCardTypeByNumber("3400-0000-0000-009", "AMEX", false); - testCardTypeByNumber("4111-1111-1111-1111", "VISA", false); + testCardTypeByNumber( "5111-1111-1111-1118", "MasterCard", true ); + testCardTypeByNumber( "6111-1111-1111-1116", "Discover", false ); + testCardTypeByNumber( "3400-0000-0000-009", "AMEX", false ); + testCardTypeByNumber( "4111-1111-1111-1111", "VISA", false ); }); +/* function fillFormWithValuesAndExpect(formSelector, inputValues, expected) { - for (i=0; i < inputValues.length; i++) { - $(formSelector + ' input:eq(' + i + ')').val(inputValues[i]); + var i, actual; + + for (i = 0; i < inputValues.length; i++) { + $(formSelector + " input:eq(" + i + ")").val(inputValues[i]); } - var actual = $(formSelector).valid(); - equal(actual, expected, $.format("Filled inputs of form '{0}' with {1} values ({2})", formSelector, inputValues.length, inputValues.toString())); + actual = $(formSelector).valid(); + equal(actual, expected, $.validator.format("Filled inputs of form '{0}' with {1} values ({2})", formSelector, inputValues.length, inputValues.toString())); } -test('require_from_group', function() { +test("require_from_group", function() { $("#productInfo").validate({ rules: { - partnumber: {require_from_group: [2,".productInfo"]}, - description: {require_from_group: [2,".productInfo"]}, - discount: {require_from_group: [2,".productInfo"]} + partnumber: { require_from_group: [ 2, ".productInfo" ] }, + description: { require_from_group: [ 2, ".productInfo" ] }, + discount: { require_from_group: [ 2, ".productInfo" ] } } }); - fillFormWithValuesAndExpect('#productInfo', [], false); - fillFormWithValuesAndExpect('#productInfo', [123], false); - $('#productInfo input[type="checkbox"]').attr('checked', 'checked'); - fillFormWithValuesAndExpect('#productInfo', [123], true); - $('#productInfo input[type="checkbox"]').removeAttr('checked'); - fillFormWithValuesAndExpect('#productInfo', [123, 'widget'], true); - fillFormWithValuesAndExpect('#productInfo', [123, 'widget', 'red'], true); - fillFormWithValuesAndExpect('#productInfo', [123, 'widget', 'red'], true); + fillFormWithValuesAndExpect("#productInfo", [], false); + fillFormWithValuesAndExpect("#productInfo", [ 123 ], false); + $("#productInfo input[type='checkbox']").attr("checked", "checked"); + fillFormWithValuesAndExpect("#productInfo", [ 123 ], true); + $("#productInfo input[type='checkbox']").removeAttr("checked"); + fillFormWithValuesAndExpect("#productInfo", [ 123, "widget" ], true); + fillFormWithValuesAndExpect("#productInfo", [ 123, "widget", "red" ], true); + fillFormWithValuesAndExpect("#productInfo", [ 123, "widget", "red" ], true); }); -test('skip_or_fill_minimum', function() { +test("require_from_group preserve other rules", function() { $("#productInfo").validate({ rules: { - partnumber: {skip_or_fill_minimum: [2,".productInfo"]}, - description: {skip_or_fill_minimum: [2,".productInfo"]}, - color: {skip_or_fill_minimum: [2,".productInfo"]} + partnumber: { require_from_group: [ 2, ".productInfo" ] }, + description: { require_from_group: [ 2, ".productInfo" ] }, + color: { require_from_group: [ 2, ".productInfo" ] }, + supplier: { required: true } } }); - fillFormWithValuesAndExpect('#productInfo', [], true); - fillFormWithValuesAndExpect('#productInfo', [123], false); - fillFormWithValuesAndExpect('#productInfo', [123, 'widget'], true); - fillFormWithValuesAndExpect('#productInfo', [123, 'widget', 'red'], true); + fillFormWithValuesAndExpect("#productInfo", [], false); + fillFormWithValuesAndExpect("#productInfo", [ 123 ], false); + fillFormWithValuesAndExpect("#productInfo", [ 123, "widget" ], false); + fillFormWithValuesAndExpect("#productInfo", [ "", "", "", "Acme" ], false); + fillFormWithValuesAndExpect("#productInfo", [ 123, "", "", "Acme" ], false); + fillFormWithValuesAndExpect("#productInfo", [ 123, "widget", "", "Acme" ], true); + fillFormWithValuesAndExpect("#productInfo", [ 123, "widget", "red", "Acme" ], true); }); +test("skip_or_fill_minimum", function() { + $("#productInfo").validate({ + rules: { + partnumber: { skip_or_fill_minimum: [ 2, ".productInfo" ] }, + description: { skip_or_fill_minimum: [ 2, ".productInfo" ] }, + color: { skip_or_fill_minimum: [ 2, ".productInfo" ] } + } + }); + + fillFormWithValuesAndExpect("#productInfo", [], true); + fillFormWithValuesAndExpect("#productInfo", [ 123 ], false); + fillFormWithValuesAndExpect("#productInfo", [ 123, "widget" ], true); + fillFormWithValuesAndExpect("#productInfo", [ 123, "widget", "red" ], true); +}); + +test("skip_or_fill_minimum preserve other rules", function() { + $("#productInfo").validate({ + rules: { + partnumber: { skip_or_fill_minimum: [ 2, ".productInfo" ] }, + description: { skip_or_fill_minimum: [ 2, ".productInfo" ] }, + color: { skip_or_fill_minimum: [ 2, ".productInfo" ] }, + supplier: { required: true } + } + }); + + fillFormWithValuesAndExpect("#productInfo", [], false); + fillFormWithValuesAndExpect("#productInfo", [ "", "", "", "Acme" ], true); + fillFormWithValuesAndExpect("#productInfo", [ 123, "", "", "Acme" ], false); + fillFormWithValuesAndExpect("#productInfo", [ 123, "widget", "", "Acme" ], true); + fillFormWithValuesAndExpect("#productInfo", [ 123, "widget", "red", "Acme" ], true); +}); +*/ + test("zipcodeUS", function() { var method = methodTest("zipcodeUS"); ok( method( "12345" ), "Valid zip" ); ok( method( "12345-2345" ), "Valid zip" ); + ok( method( "90210-4567" ), "Valid zip" ); ok(!method( "1" ), "Invalid zip" ); ok(!method( "1234" ), "Invalid zip" ); ok(!method( "123-23" ), "Invalid zip" ); ok(!method( "12345-43" ), "Invalid zip" ); + ok(!method( "123456-7890" ), "Invalid zip" ); +}); + +test("nifES", function() { + var method = methodTest("nifES"); + ok( method( "11441059P" ), "NIF valid" ); + ok( method( "80054306T" ), "NIF valid" ); + ok( method( "76048581R" ), "NIF valid" ); + ok( method( "28950849J" ), "NIF valid" ); + ok( method( "34048598L" ), "NIF valid" ); + ok( method( "28311529R" ), "NIF valid" ); + ok( method( "34673804Q" ), "NIF valid" ); + ok( method( "92133247P" ), "NIF valid" ); + ok( method( "77149717N" ), "NIF valid" ); + ok( method( "15762034L" ), "NIF valid" ); + ok( method( "05122654W" ), "NIF valid" ); + ok( method( "05122654w" ), "NIF valid: lower case" ); + ok(!method( "1144105R" ), "NIF invalid: less than 8 digits without zero" ); + ok(!method( "11441059 R" ), "NIF invalid: white space" ); + ok(!method( "11441059" ), "NIF invalid: no letter" ); + ok(!method( "11441059PR" ), "NIF invalid: two letters" ); + ok(!method( "11440059R" ), "NIF invalid: wrong number" ); + ok(!method( "11441059S" ), "NIF invalid: wrong letter" ); + ok(!method( "114410598R" ), "NIF invalid: > 8 digits" ); + ok(!method( "11441059-R" ), "NIF invalid: dash" ); + ok(!method( "asdasdasd" ), "NIF invalid: all letters" ); + ok(!method( "11.144.059R" ), "NIF invalid: two dots" ); + ok(!method( "05.122.654R" ), "NIF invalid: starts with 0 and dots" ); + ok(!method( "5.122.654-R" ), "NIF invalid: dots and dash" ); + ok(!method( "05.122.654-R" ), "NIF invalid: starts with zero and dot and dash" ); +}); + +test("nieES", function() { + var method = methodTest("nieES"); + ok( method( "X0093999K" ), "NIE valid" ); + ok( method( "X1923000Q" ), "NIE valid" ); + ok( method( "Z9669587R" ), "NIE valid" ); + ok( method( "Z8945005B" ), "NIE valid" ); + ok( method( "Z6663465W" ), "NIE valid" ); + ok( method( "Y7875935J" ), "NIE valid" ); + ok( method( "X3390130E" ), "NIE valid" ); + ok( method( "Y7699182S" ), "NIE valid" ); + ok( method( "Y1524243R" ), "NIE valid" ); + ok( method( "X3744072V" ), "NIE valid" ); + ok( method( "X7436800A" ), "NIE valid" ); + ok( method( "y7875935j" ), "NIE valid: lower case" ); + ok(!method( "X0093999 K" ), "NIE inválido: white space" ); + ok(!method( "X 0093999 K" ), "NIE inválido: white space" ); + ok(!method( "11441059" ), "NIE inválido: no letter" ); + ok(!method( "11441059PR" ), "NIE inválido: two letters" ); + ok(!method( "11440059R" ), "NIE inválido: wrong number" ); + ok(!method( "11441059S" ), "NIE inválido: wrong letter" ); + ok(!method( "114410598R" ), "NIE inválido: > 8 digits" ); + ok(!method( "11441059-R" ), "NIE inválido: dash" ); + ok(!method( "asdasdasd" ), "NIE inválido: all letters" ); + ok(!method( "11.144.059R" ), "NIE inválido: two dots" ); + ok(!method( "05.122.654R" ), "NIE inválido: starts with 0 and dots" ); + ok(!method( "5.122.654-R" ), "NIE inválido: dots and dash" ); + ok(!method( "05.122.654-R" ), "NIE inválido: starts with zero and dot and dash" ); +}); + +test("cifES", function() { + var method = methodTest("cifES"); + ok( method( "A79082244" ), "CIF valid" ); + ok( method( "A60917978" ), "CIF valid" ); + ok( method( "A39000013" ), "CIF valid" ); + ok( method( "B43522192" ), "CIF valid" ); + ok( method( "B38624334" ), "CIF valid" ); + ok( method( "G72102064" ), "CIF valid" ); + ok( method( "F41190612" ), "CIF valid" ); + ok( method( "J85081081" ), "CIF valid" ); + ok( method( "S98038813" ), "CIF valid" ); + ok( method( "G32937757" ), "CIF valid" ); + ok( method( "B46125746" ), "CIF valid" ); + ok( method( "C27827559" ), "CIF valid" ); + ok( method( "E48911572" ), "CIF valid" ); + ok( method( "s98038813" ), "CIF valid: lower case" ); + ok(!method( "K48911572" ), "CIF invalid: starts with K" ); + ok(!method( "L48911572" ), "CIF invalid: starts with L" ); + ok(!method( "M48911572" ), "CIF invalid: starts with M" ); + ok(!method( "X48911572" ), "CIF invalid: starts with X" ); + ok(!method( "Y48911572" ), "CIF invalid: starts with Y" ); + ok(!method( "Z48911572" ), "CIF invalid: starts with Z" ); + ok(!method( "M15661515" ), "CIF invalid" ); + ok(!method( "Z98038813" ), "CIF invalid: wrong letter" ); + ok(!method( "B 43522192" ), "CIF invalid: white spaces" ); + ok(!method( "43522192" ), "CIF invalid: missing letter" ); + ok(!method( "BB43522192" ), "CIF invalid: two letters" ); + ok(!method( "B53522192" ), "CIF invalid: wrong number" ); + ok(!method( "B433522192" ), "CIF invalid: > 8 digits" ); + ok(!method( "B3522192" ), "CIF invalid: < 8 digits" ); + ok(!method( "B-43522192" ), "CIF invalid: dash" ); + ok(!method( "Basdasdas" ), "CIF invalid: all letters" ); + ok(!method( "B43.522.192" ), "CIF invalid: dots" ); + ok(!method( "B-43.522.192" ), "CIF invalid: dots and dash" ); +}); + +test("maxWords", function() { + var method = methodTest("maxWords"), + maxWords = 6; + + ok( method( "I am a sentence", maxWords), "Max Words"); + ok(!method( "I'm way too long for this sentence!", maxWords), "Too many words"); + ok(method( "Don’t “count” me as too long", maxWords), "Right amount of words with smartquotes"); + ok(!method( "But you can “count” me as too long", maxWords), "Too many words with smartquotes"); + ok(method( "<div>Don’t “count” me as too long</div>", maxWords), "Right amount of words with smartquotes w/ HTML"); + ok(!method( "<div>But you can “count” me as too long</div>", maxWords), "Too many words with smartquotes w/ HTML"); +}); + +test("minWords", function() { + var method = methodTest("minWords"), + minWords = 6; + + ok(!method( "I am a short sentence", minWords), "Max Words"); + ok( method( "I'm way too long for this sentence!", minWords), "Too many words"); + ok(!method( "Don’t “count” me as short.", minWords), "Right amount of words with smartquotes"); + ok( method( "But you can “count” me as too short", minWords), "Too many words with smartquotes"); + ok(!method( "<div>“Count” me as too short.</div>", minWords), "Right amount of words with smartquotes w/ HTML"); + ok( method( "<div>But you can “count” me as too long</div>", minWords), "Too many words with smartquotes w/ HTML"); +}); + +test("rangeWords", function() { + var method = methodTest("rangeWords"), + rangeWords = [ 3, 6 ]; + + ok(!method( "I'm going to be longer than “six words!”", rangeWords), "Longer than 6 with smartquotes"); + ok( method( "I'm just the right amount!", rangeWords), "In between"); + ok( method( "Super short sentence’s.", rangeWords), "Low end"); + ok(!method( "I", rangeWords), "Too short"); + ok( method( "<div>“Count” me as perfect.</div>", rangeWords), "Right amount of words with smartquotes w/ HTML"); + ok(!method( "<div>But you can “count” me as too long</div>", rangeWords), "Too many words with smartquotes w/ HTML"); +}); + +test("currency", function() { // Works with any symbol + var method = methodTest( "currency" ); + ok( method( "£9", "£"), "Symbol no decimal" ); + ok( method( "£9.9", "£"), "£, one decimal" ); + ok( method( "£9.99", "£"), "£, two decimal" ); + ok( method( "£9.90", "£"), "Valid currency" ); + ok( method( "£9,999.9", "£"), "£, thousand, comma separator, one decimal" ); + ok( method( "£9,999.99", "£"), "£, thousand, comma separator, two decimal" ); + ok( method( "£9,999,999.9", "£"), "£, million, comma separators, one decimal" ); + ok( method( "9", [ "£", false ]), "Valid currency" ); + ok( method( "9.9", [ "£", false ]), "Valid currency" ); + ok( method( "9.99", [ "£", false ]), "Valid currency" ); + ok( method( "9.90", [ "£", false ]), "Valid currency" ); + ok( method( "9,999.9", [ "£", false ]), "Valid currency" ); + ok( method( "9,999.99", [ "£", false ]), "Valid currency" ); + ok( method( "9,999,999.9", [ "£", false ]), "Valid currency" ); + ok(!method( "9,", "£"), "Invalid currency" ); + ok(!method( "9,99.99", "£"), "Invalid currency" ); + ok(!method( "9,", "£"), "Invalid currency" ); + ok(!method( "9.999", "£"), "Invalid currency" ); + ok(!method( "9.999", "£"), "Invalid currency" ); + ok(!method( "9.99,9", "£"), "Invalid currency" ); +}); + +test("postalCodeCA", function() { + var method = methodTest("postalCodeCA"); + ok( method( "H0H 0H0"), "Valid CA Postal Code; Single space" ); + ok( !method( "H0H0H0"), "Inalid CA Postal Code; No space" ); + ok( !method( "H0H-0H0"), "Invalid CA Postal Code; Single dash" ); + ok( !method( "H0H 0H"), "Invalid CA Postal Code; Too Short" ); + ok( !method( "Z0H 0H"), "Invalid CA Postal Code; Only 'ABCEGHJKLMNPRSTVXY' are valid starting characters" ); + ok( !method( "h0h 0h0"), "Invalid CA Postal Code; Only upper case characters" ); +}); + +test("stateUS", function() { + var method = methodTest("stateUS"); + ok( method( "AZ" ), "Valid US state" ); + ok( method( "OH" ), "Valid US state" ); + ok( method( "DC" ), "Valid US state" ); + ok( method( "PR", { includeTerritories: true } ), "Valid US territory" ); + ok( method( "AA", { includeMilitary: true } ), "Valid US military zone" ); + ok( method( "me", { caseSensitive: false } ), "Valid US state" ); + ok(!method( "az", { caseSensitive: true } ), "Must be capital letters" ); + ok(!method( "mp", { caseSensitive: false, includeTerritories: false } ), "US territories not allowed" ); +}); + +test("postalcodeBR", function() { + var method = methodTest("postalcodeBR"); + ok( method( "99999-999"), "Valid BR Postal Code"); + ok( method( "99999999"), "Valid BR Postal Code"); + ok( method( "99.999-999"), "Valid BR Postal Code"); + ok( !method( "99.999999"), "Invalid BR Postal Code"); +}); + +test("cpfBR", function() { + var method = methodTest("cpfBR"); + ok( method( "11144477735"), "Valid CPF Number"); + ok( method( "263.946.533-30"), "Valid CPF Number"); + ok( method( "325 861 044 47"), "Valid CPF Number"); + ok( method( "859-684-732-40"), "Valid CPF Number"); + ok( !method( "99999999999"), "Invalid CPF Number: dump data"); + ok( !method( "1114447773"), "Invalid CPF Number: < 11 digits"); + ok( !method( "111444777355"), "Invalid CPF Number: > 11 digits"); + ok( !method( "11144477715"), "Invalid CPF Number: 1st check number failed"); + ok( !method( "11144477737"), "Invalid CPF Number: 2nd check number failed"); }); })(jQuery); diff --git a/wqflask/wqflask/static/new/packages/ValidationPlugin/test/qunit/qunit.css b/wqflask/wqflask/static/new/packages/ValidationPlugin/test/qunit/qunit.css index b948bae1..f1dcd4e1 100755..100644 --- a/wqflask/wqflask/static/new/packages/ValidationPlugin/test/qunit/qunit.css +++ b/wqflask/wqflask/static/new/packages/ValidationPlugin/test/qunit/qunit.css @@ -1,11 +1,12 @@ -/** - * QUnit v1.5.0 - A JavaScript Unit Testing Framework +/*! + * QUnit 1.18.0 + * http://qunitjs.com/ * - * http://docs.jquery.com/QUnit + * Copyright jQuery Foundation and other contributors + * Released under the MIT license + * http://jquery.org/license * - * Copyright (c) 2012 John Resig, Jörn Zaefferer - * Dual licensed under the MIT (MIT-LICENSE.txt) - * or GPL (GPL-LICENSE.txt) licenses. + * Date: 2015-04-03T10:23Z */ /** Font Family and Sizes */ @@ -20,7 +21,7 @@ /** Resets */ -#qunit-tests, #qunit-tests ol, #qunit-header, #qunit-banner, #qunit-userAgent, #qunit-testresult { +#qunit-tests, #qunit-header, #qunit-banner, #qunit-userAgent, #qunit-testresult, #qunit-modulefilter { margin: 0; padding: 0; } @@ -31,31 +32,29 @@ #qunit-header { padding: 0.5em 0 0.5em 1em; - color: #8699a4; - background-color: #0d3349; + color: #8699A4; + background-color: #0D3349; font-size: 1.5em; line-height: 1em; - font-weight: normal; + font-weight: 400; - border-radius: 15px 15px 0 0; - -moz-border-radius: 15px 15px 0 0; - -webkit-border-top-right-radius: 15px; - -webkit-border-top-left-radius: 15px; + border-radius: 5px 5px 0 0; } #qunit-header a { text-decoration: none; - color: #c2ccd1; + color: #C2CCD1; } #qunit-header a:hover, #qunit-header a:focus { - color: #fff; + color: #FFF; } -#qunit-header label { +#qunit-testrunner-toolbar label { display: inline-block; + padding: 0 0.5em 0 0.1em; } #qunit-banner { @@ -63,18 +62,34 @@ } #qunit-testrunner-toolbar { - padding: 0.5em 0 0.5em 2em; + padding: 0.5em 1em 0.5em 1em; color: #5E740B; - background-color: #eee; + background-color: #EEE; + overflow: hidden; } #qunit-userAgent { - padding: 0.5em 0 0.5em 2.5em; - background-color: #2b81af; - color: #fff; + padding: 0.5em 1em 0.5em 1em; + background-color: #2B81AF; + color: #FFF; text-shadow: rgba(0, 0, 0, 0.5) 2px 2px 1px; } +#qunit-modulefilter-container { + float: right; + padding: 0.2em; +} + +.qunit-url-config { + display: inline-block; + padding: 0.1em; +} + +.qunit-filter { + display: block; + float: right; + margin-left: 1em; +} /** Tests: Pass/Fail */ @@ -83,53 +98,83 @@ } #qunit-tests li { - padding: 0.4em 0.5em 0.4em 2.5em; - border-bottom: 1px solid #fff; + padding: 0.4em 1em 0.4em 1em; + border-bottom: 1px solid #FFF; list-style-position: inside; } -#qunit-tests.hidepass li.pass, #qunit-tests.hidepass li.running { +#qunit-tests > li { display: none; } +#qunit-tests li.running, +#qunit-tests li.pass, +#qunit-tests li.fail, +#qunit-tests li.skipped { + display: list-item; +} + +#qunit-tests.hidepass li.running, +#qunit-tests.hidepass li.pass { + visibility: hidden; + position: absolute; + width: 0px; + height: 0px; + padding: 0; + border: 0; + margin: 0; +} + #qunit-tests li strong { cursor: pointer; } +#qunit-tests li.skipped strong { + cursor: default; +} + #qunit-tests li a { padding: 0.5em; - color: #c2ccd1; + color: #C2CCD1; text-decoration: none; } + +#qunit-tests li p a { + padding: 0.25em; + color: #6B6464; +} #qunit-tests li a:hover, #qunit-tests li a:focus { color: #000; } -#qunit-tests ol { +#qunit-tests li .runtime { + float: right; + font-size: smaller; +} + +.qunit-assert-list { margin-top: 0.5em; padding: 0.5em; - background-color: #fff; + background-color: #FFF; - border-radius: 15px; - -moz-border-radius: 15px; - -webkit-border-radius: 15px; + border-radius: 5px; +} - box-shadow: inset 0px 2px 13px #999; - -moz-box-shadow: inset 0px 2px 13px #999; - -webkit-box-shadow: inset 0px 2px 13px #999; +.qunit-collapsed { + display: none; } #qunit-tests table { border-collapse: collapse; - margin-top: .2em; + margin-top: 0.2em; } #qunit-tests th { text-align: right; vertical-align: top; - padding: 0 .5em 0 0; + padding: 0 0.5em 0 0; } #qunit-tests td { @@ -143,27 +188,26 @@ } #qunit-tests del { - background-color: #e0f2be; - color: #374e0c; + background-color: #E0F2BE; + color: #374E0C; text-decoration: none; } #qunit-tests ins { - background-color: #ffcaca; + background-color: #FFCACA; color: #500; text-decoration: none; } /*** Test Counts */ -#qunit-tests b.counts { color: black; } +#qunit-tests b.counts { color: #000; } #qunit-tests b.passed { color: #5E740B; } #qunit-tests b.failed { color: #710909; } #qunit-tests li li { - margin: 0.5em; - padding: 0.4em 0.5em 0.4em 0.5em; - background-color: #fff; + padding: 5px; + background-color: #FFF; border-bottom: none; list-style-position: inside; } @@ -171,16 +215,16 @@ /*** Passing Styles */ #qunit-tests li li.pass { - color: #5E740B; - background-color: #fff; - border-left: 26px solid #C6E746; + color: #3C510C; + background-color: #FFF; + border-left: 10px solid #C6E746; } #qunit-tests .pass { color: #528CE0; background-color: #D2E0E6; } #qunit-tests .pass .test-name { color: #366097; } #qunit-tests .pass .test-actual, -#qunit-tests .pass .test-expected { color: #999999; } +#qunit-tests .pass .test-expected { color: #999; } #qunit-banner.qunit-pass { background-color: #C6E746; } @@ -188,40 +232,52 @@ #qunit-tests li li.fail { color: #710909; - background-color: #fff; - border-left: 26px solid #EE5757; + background-color: #FFF; + border-left: 10px solid #EE5757; white-space: pre; } #qunit-tests > li:last-child { - border-radius: 0 0 15px 15px; - -moz-border-radius: 0 0 15px 15px; - -webkit-border-bottom-right-radius: 15px; - -webkit-border-bottom-left-radius: 15px; + border-radius: 0 0 5px 5px; } -#qunit-tests .fail { color: #000000; background-color: #EE5757; } +#qunit-tests .fail { color: #000; background-color: #EE5757; } #qunit-tests .fail .test-name, -#qunit-tests .fail .module-name { color: #000000; } +#qunit-tests .fail .module-name { color: #000; } #qunit-tests .fail .test-actual { color: #EE5757; } -#qunit-tests .fail .test-expected { color: green; } +#qunit-tests .fail .test-expected { color: #008000; } #qunit-banner.qunit-fail { background-color: #EE5757; } +/*** Skipped tests */ + +#qunit-tests .skipped { + background-color: #EBECE9; +} + +#qunit-tests .qunit-skipped-label { + background-color: #F4FF77; + display: inline-block; + font-style: normal; + color: #366097; + line-height: 1.8em; + padding: 0 0.5em; + margin: -0.4em 0.4em -0.4em 0; +} /** Result */ #qunit-testresult { - padding: 0.5em 0.5em 0.5em 2.5em; + padding: 0.5em 1em 0.5em 1em; - color: #2b81af; + color: #2B81AF; background-color: #D2E0E6; - border-bottom: 1px solid white; + border-bottom: 1px solid #FFF; } #qunit-testresult .module-name { - font-weight: bold; + font-weight: 700; } /** Fixture */ diff --git a/wqflask/wqflask/static/new/packages/ValidationPlugin/test/qunit/qunit.js b/wqflask/wqflask/static/new/packages/ValidationPlugin/test/qunit/qunit.js index 66dd7215..f3542ca9 100755..100644 --- a/wqflask/wqflask/static/new/packages/ValidationPlugin/test/qunit/qunit.js +++ b/wqflask/wqflask/static/new/packages/ValidationPlugin/test/qunit/qunit.js @@ -1,980 +1,567 @@ -/** - * QUnit v1.5.0 - A JavaScript Unit Testing Framework +/*! + * QUnit 1.18.0 + * http://qunitjs.com/ * - * http://docs.jquery.com/QUnit + * Copyright jQuery Foundation and other contributors + * Released under the MIT license + * http://jquery.org/license * - * Copyright (c) 2012 John Resig, Jörn Zaefferer - * Dual licensed under the MIT (MIT-LICENSE.txt) - * or GPL (GPL-LICENSE.txt) licenses. + * Date: 2015-04-03T10:23Z */ -(function(window) { +(function( window ) { -var defined = { - setTimeout: typeof window.setTimeout !== "undefined", - sessionStorage: (function() { - var x = "qunit-test-string"; - try { - sessionStorage.setItem(x, x); - sessionStorage.removeItem(x); - return true; - } catch(e) { - return false; - } - }()) -}; - -var testId = 0, +var QUnit, + config, + onErrorFnPrev, + loggingCallbacks = {}, + fileName = ( sourceFromStacktrace( 0 ) || "" ).replace( /(:\d+)+\)?/, "" ).replace( /.+\//, "" ), toString = Object.prototype.toString, - hasOwn = Object.prototype.hasOwnProperty; - -var Test = function(name, testName, expected, async, callback) { - this.name = name; - this.testName = testName; - this.expected = expected; - this.async = async; - this.callback = callback; - this.assertions = []; -}; -Test.prototype = { - init: function() { - var tests = id("qunit-tests"); - if (tests) { - var b = document.createElement("strong"); - b.innerHTML = "Running " + this.name; - var li = document.createElement("li"); - li.appendChild( b ); - li.className = "running"; - li.id = this.id = "test-output" + testId++; - tests.appendChild( li ); + hasOwn = Object.prototype.hasOwnProperty, + // Keep a local reference to Date (GH-283) + Date = window.Date, + now = Date.now || function() { + return new Date().getTime(); + }, + globalStartCalled = false, + runStarted = false, + setTimeout = window.setTimeout, + clearTimeout = window.clearTimeout, + defined = { + document: window.document !== undefined, + setTimeout: window.setTimeout !== undefined, + sessionStorage: (function() { + var x = "qunit-test-string"; + try { + sessionStorage.setItem( x, x ); + sessionStorage.removeItem( x ); + return true; + } catch ( e ) { + return false; + } + }()) + }, + /** + * Provides a normalized error string, correcting an issue + * with IE 7 (and prior) where Error.prototype.toString is + * not properly implemented + * + * Based on http://es5.github.com/#x15.11.4.4 + * + * @param {String|Error} error + * @return {String} error message + */ + errorString = function( error ) { + var name, message, + errorString = error.toString(); + if ( errorString.substring( 0, 7 ) === "[object" ) { + name = error.name ? error.name.toString() : "Error"; + message = error.message ? error.message.toString() : ""; + if ( name && message ) { + return name + ": " + message; + } else if ( name ) { + return name; + } else if ( message ) { + return message; + } else { + return "Error"; + } + } else { + return errorString; } }, - setup: function() { - if (this.module != config.previousModule) { - if ( config.previousModule ) { - runLoggingCallbacks('moduleDone', QUnit, { - name: config.previousModule, - failed: config.moduleStats.bad, - passed: config.moduleStats.all - config.moduleStats.bad, - total: config.moduleStats.all - } ); + /** + * Makes a clone of an object using only Array or Object as base, + * and copies over the own enumerable properties. + * + * @param {Object} obj + * @return {Object} New object with only the own properties (recursively). + */ + objectValues = function( obj ) { + var key, val, + vals = QUnit.is( "array", obj ) ? [] : {}; + for ( key in obj ) { + if ( hasOwn.call( obj, key ) ) { + val = obj[ key ]; + vals[ key ] = val === Object( val ) ? objectValues( val ) : val; } - config.previousModule = this.module; - config.moduleStats = { all: 0, bad: 0 }; - runLoggingCallbacks( 'moduleStart', QUnit, { - name: this.module - } ); - } else if (config.autorun) { - runLoggingCallbacks( 'moduleStart', QUnit, { - name: this.module - } ); } + return vals; + }; - config.current = this; - this.testEnvironment = extend({ - setup: function() {}, - teardown: function() {} - }, this.moduleTestEnvironment); +QUnit = {}; - runLoggingCallbacks( 'testStart', QUnit, { - name: this.testName, - module: this.module - }); +/** + * Config object: Maintain internal state + * Later exposed as QUnit.config + * `config` initialized at top of scope + */ +config = { + // The queue of tests to run + queue: [], - // allow utility functions to access the current test environment - // TODO why?? - QUnit.current_testEnvironment = this.testEnvironment; + // block until document ready + blocking: true, - if ( !config.pollution ) { - saveGlobal(); - } - if ( config.notrycatch ) { - this.testEnvironment.setup.call(this.testEnvironment); - return; - } - try { - this.testEnvironment.setup.call(this.testEnvironment); - } catch(e) { - QUnit.pushFailure( "Setup failed on " + this.testName + ": " + e.message, extractStacktrace( e, 1 ) ); - } - }, - run: function() { - config.current = this; + // by default, run previously failed tests first + // very useful in combination with "Hide passed tests" checked + reorder: true, - var running = id("qunit-testresult"); + // by default, modify document.title when suite is done + altertitle: true, - if ( running ) { - running.innerHTML = "Running: <br/>" + this.name; - } + // by default, scroll to top of the page when suite is done + scrolltop: true, - if ( this.async ) { - QUnit.stop(); - } + // when enabled, all tests must call expect() + requireExpects: false, - if ( config.notrycatch ) { - this.callback.call(this.testEnvironment); - return; - } - try { - this.callback.call(this.testEnvironment); - } catch(e) { - QUnit.pushFailure( "Died on test #" + (this.assertions.length + 1) + ": " + e.message, extractStacktrace( e, 1 ) ); - // else next test will carry the responsibility - saveGlobal(); + // depth up-to which object will be dumped + maxDepth: 5, - // Restart the tests if they're blocking - if ( config.blocking ) { - QUnit.start(); - } - } - }, - teardown: function() { - config.current = this; - if ( config.notrycatch ) { - this.testEnvironment.teardown.call(this.testEnvironment); - return; - } else { - try { - this.testEnvironment.teardown.call(this.testEnvironment); - } catch(e) { - QUnit.pushFailure( "Teardown failed on " + this.testName + ": " + e.message, extractStacktrace( e, 1 ) ); - } - } - checkPollution(); - }, - finish: function() { - config.current = this; - if ( this.expected != null && this.expected != this.assertions.length ) { - QUnit.pushFailure( "Expected " + this.expected + " assertions, but " + this.assertions.length + " were run" ); - } else if ( this.expected == null && !this.assertions.length ) { - QUnit.pushFailure( "Expected at least one assertion, but none were run - call expect(0) to accept zero assertions." ); + // add checkboxes that are persisted in the query-string + // when enabled, the id is set to `true` as a `QUnit.config` property + urlConfig: [ + { + id: "hidepassed", + label: "Hide passed tests", + tooltip: "Only show tests and assertions that fail. Stored as query-strings." + }, + { + id: "noglobals", + label: "Check for Globals", + tooltip: "Enabling this will test if any test introduces new properties on the " + + "`window` object. Stored as query-strings." + }, + { + id: "notrycatch", + label: "No try-catch", + tooltip: "Enabling this will run tests outside of a try-catch block. Makes debugging " + + "exceptions in IE reasonable. Stored as query-strings." } + ], - var good = 0, bad = 0, - li, i, - tests = id("qunit-tests"); + // Set of all modules. + modules: [], - config.stats.all += this.assertions.length; - config.moduleStats.all += this.assertions.length; - - if ( tests ) { - var ol = document.createElement("ol"); + // The first unnamed module + currentModule: { + name: "", + tests: [] + }, - for ( i = 0; i < this.assertions.length; i++ ) { - var assertion = this.assertions[i]; + callbacks: {} +}; - li = document.createElement("li"); - li.className = assertion.result ? "pass" : "fail"; - li.innerHTML = assertion.message || (assertion.result ? "okay" : "failed"); - ol.appendChild( li ); +// Push a loose unnamed module to the modules collection +config.modules.push( config.currentModule ); - if ( assertion.result ) { - good++; - } else { - bad++; - config.stats.bad++; - config.moduleStats.bad++; - } - } +// Initialize more QUnit.config and QUnit.urlParams +(function() { + var i, current, + location = window.location || { search: "", protocol: "file:" }, + params = location.search.slice( 1 ).split( "&" ), + length = params.length, + urlParams = {}; - // store result when possible - if ( QUnit.config.reorder && defined.sessionStorage ) { - if (bad) { - sessionStorage.setItem("qunit-test-" + this.module + "-" + this.testName, bad); - } else { - sessionStorage.removeItem("qunit-test-" + this.module + "-" + this.testName); - } - } + if ( params[ 0 ] ) { + for ( i = 0; i < length; i++ ) { + current = params[ i ].split( "=" ); + current[ 0 ] = decodeURIComponent( current[ 0 ] ); - if (bad === 0) { - ol.style.display = "none"; + // allow just a key to turn on a flag, e.g., test.html?noglobals + current[ 1 ] = current[ 1 ] ? decodeURIComponent( current[ 1 ] ) : true; + if ( urlParams[ current[ 0 ] ] ) { + urlParams[ current[ 0 ] ] = [].concat( urlParams[ current[ 0 ] ], current[ 1 ] ); + } else { + urlParams[ current[ 0 ] ] = current[ 1 ]; } + } + } - var b = document.createElement("strong"); - b.innerHTML = this.name + " <b class='counts'>(<b class='failed'>" + bad + "</b>, <b class='passed'>" + good + "</b>, " + this.assertions.length + ")</b>"; + if ( urlParams.filter === true ) { + delete urlParams.filter; + } - var a = document.createElement("a"); - a.innerHTML = "Rerun"; - a.href = QUnit.url({ filter: getText([b]).replace(/\([^)]+\)$/, "").replace(/(^\s*|\s*$)/g, "") }); + QUnit.urlParams = urlParams; - addEvent(b, "click", function() { - var next = b.nextSibling.nextSibling, - display = next.style.display; - next.style.display = display === "none" ? "block" : "none"; - }); + // String search anywhere in moduleName+testName + config.filter = urlParams.filter; - addEvent(b, "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 = QUnit.url({ filter: getText([target]).replace(/\([^)]+\)$/, "").replace(/(^\s*|\s*$)/g, "") }); - } - }); + if ( urlParams.maxDepth ) { + config.maxDepth = parseInt( urlParams.maxDepth, 10 ) === -1 ? + Number.POSITIVE_INFINITY : + urlParams.maxDepth; + } - li = id(this.id); - li.className = bad ? "fail" : "pass"; - li.removeChild( li.firstChild ); - li.appendChild( b ); - li.appendChild( a ); - li.appendChild( ol ); + config.testId = []; + if ( urlParams.testId ) { - } else { - for ( i = 0; i < this.assertions.length; i++ ) { - if ( !this.assertions[i].result ) { - bad++; - config.stats.bad++; - config.moduleStats.bad++; - } - } + // Ensure that urlParams.testId is an array + urlParams.testId = decodeURIComponent( urlParams.testId ).split( "," ); + for ( i = 0; i < urlParams.testId.length; i++ ) { + config.testId.push( urlParams.testId[ i ] ); } + } - QUnit.reset(); + // Figure out if we're running the tests from a server or not + QUnit.isLocal = location.protocol === "file:"; - runLoggingCallbacks( 'testDone', QUnit, { - name: this.testName, - module: this.module, - failed: bad, - passed: this.assertions.length - bad, - total: this.assertions.length - } ); - }, + // Expose the current QUnit version + QUnit.version = "1.18.0"; +}()); - queue: function() { - var test = this; - synchronize(function() { - test.init(); - }); - function run() { - // each of these can by async - synchronize(function() { - test.setup(); - }); - synchronize(function() { - test.run(); - }); - synchronize(function() { - test.teardown(); - }); - synchronize(function() { - test.finish(); - }); - } - // defer when previous test run passed, if storage is available - var bad = QUnit.config.reorder && defined.sessionStorage && +sessionStorage.getItem("qunit-test-" + this.module + "-" + this.testName); - if (bad) { - run(); - } else { - synchronize(run, true); - } - } +// Root QUnit object. +// `QUnit` initialized at top of scope +extend( QUnit, { -}; + // call on start of module test to prepend name to all tests + module: function( name, testEnvironment ) { + var currentModule = { + name: name, + testEnvironment: testEnvironment, + tests: [] + }; -var QUnit = { + // DEPRECATED: handles setup/teardown functions, + // beforeEach and afterEach should be used instead + if ( testEnvironment && testEnvironment.setup ) { + testEnvironment.beforeEach = testEnvironment.setup; + delete testEnvironment.setup; + } + if ( testEnvironment && testEnvironment.teardown ) { + testEnvironment.afterEach = testEnvironment.teardown; + delete testEnvironment.teardown; + } - // call on start of module test to prepend name to all tests - module: function(name, testEnvironment) { - config.currentModule = name; - config.currentModuleTestEnviroment = testEnvironment; + config.modules.push( currentModule ); + config.currentModule = currentModule; }, - asyncTest: function(testName, expected, callback) { + // DEPRECATED: QUnit.asyncTest() will be removed in QUnit 2.0. + asyncTest: function( testName, expected, callback ) { if ( arguments.length === 2 ) { callback = expected; expected = null; } - QUnit.test(testName, expected, callback, true); + QUnit.test( testName, expected, callback, true ); }, - test: function(testName, expected, callback, async) { - var name = '<span class="test-name">' + escapeInnerText(testName) + '</span>'; + test: function( testName, expected, callback, async ) { + var test; if ( arguments.length === 2 ) { callback = expected; expected = null; } - if ( config.currentModule ) { - name = '<span class="module-name">' + config.currentModule + "</span>: " + name; - } - - if ( !validTest(config.currentModule + ": " + testName) ) { - return; - } + test = new Test({ + testName: testName, + expected: expected, + async: async, + callback: callback + }); - var test = new Test(name, testName, expected, async, callback); - test.module = config.currentModule; - test.moduleTestEnvironment = config.currentModuleTestEnviroment; test.queue(); }, - // Specify the number of expected assertions to gurantee that failed test (no assertions are run at all) don't slip through. - expect: function(asserts) { - config.current.expected = asserts; - }, - - // Asserts true. - // @example ok( "asdfasdf".length > 5, "There must be at least 5 chars" ); - ok: function(result, msg) { - if (!config.current) { - throw new Error("ok() assertion outside test context, was " + sourceFromStacktrace(2)); - } - result = !!result; - var details = { - result: result, - message: msg - }; - msg = escapeInnerText(msg || (result ? "okay" : "failed")); - if ( !result ) { - var source = sourceFromStacktrace(2); - if (source) { - details.source = source; - msg += '<table><tr class="test-source"><th>Source: </th><td><pre>' + escapeInnerText(source) + '</pre></td></tr></table>'; - } - } - runLoggingCallbacks( 'log', QUnit, details ); - config.current.assertions.push({ - result: result, - message: msg + skip: function( testName ) { + var test = new Test({ + testName: testName, + skip: true }); - }, - - // Checks that the first two arguments are equal, with an optional message. Prints out both actual and expected values. - // @example equal( format("Received {0} bytes.", 2), "Received 2 bytes." ); - equal: function(actual, expected, message) { - QUnit.push(expected == actual, actual, expected, message); - }, - - notEqual: function(actual, expected, message) { - QUnit.push(expected != actual, actual, expected, message); - }, - - deepEqual: function(actual, expected, message) { - QUnit.push(QUnit.equiv(actual, expected), actual, expected, message); - }, - - notDeepEqual: function(actual, expected, message) { - QUnit.push(!QUnit.equiv(actual, expected), actual, expected, message); - }, - - strictEqual: function(actual, expected, message) { - QUnit.push(expected === actual, actual, expected, message); - }, - notStrictEqual: function(actual, expected, message) { - QUnit.push(expected !== actual, actual, expected, message); + test.queue(); }, - raises: function(block, expected, message) { - var actual, ok = false; - - if (typeof expected === 'string') { - message = expected; - expected = null; - } - - try { - block.call(config.current.testEnvironment); - } catch (e) { - actual = e; - } - - if (actual) { - // we don't want to validate thrown error - if (!expected) { - ok = true; - // expected is a regexp - } else if (QUnit.objectType(expected) === "regexp") { - ok = expected.test(actual); - // expected is a constructor - } else if (actual instanceof expected) { - ok = true; - // expected is a validation function which returns true is validation passed - } else if (expected.call({}, actual) === true) { - ok = true; + // DEPRECATED: The functionality of QUnit.start() will be altered in QUnit 2.0. + // In QUnit 2.0, invoking it will ONLY affect the `QUnit.config.autostart` blocking behavior. + start: function( count ) { + var globalStartAlreadyCalled = globalStartCalled; + + if ( !config.current ) { + globalStartCalled = true; + + if ( runStarted ) { + throw new Error( "Called start() outside of a test context while already started" ); + } else if ( globalStartAlreadyCalled || count > 1 ) { + throw new Error( "Called start() outside of a test context too many times" ); + } else if ( config.autostart ) { + throw new Error( "Called start() outside of a test context when " + + "QUnit.config.autostart was true" ); + } else if ( !config.pageLoaded ) { + + // The page isn't completely loaded yet, so bail out and let `QUnit.load` handle it + config.autostart = true; + return; } - } - - QUnit.ok(ok, message); - }, - - start: function(count) { - config.semaphore -= count || 1; - if (config.semaphore > 0) { - // don't start until equal number of stop-calls - return; - } - if (config.semaphore < 0) { - // ignore if start is called more often then stop - config.semaphore = 0; - } - // A slight delay, to avoid any current callbacks - if ( defined.setTimeout ) { - window.setTimeout(function() { - if (config.semaphore > 0) { - return; - } - if ( config.timeout ) { - clearTimeout(config.timeout); - } - - config.blocking = false; - process(true); - }, 13); } else { - config.blocking = false; - process(true); - } - }, - - stop: function(count) { - config.semaphore += count || 1; - config.blocking = true; - - if ( config.testTimeout && defined.setTimeout ) { - clearTimeout(config.timeout); - config.timeout = window.setTimeout(function() { - QUnit.ok( false, "Test timed out" ); - config.semaphore = 1; - QUnit.start(); - }, config.testTimeout); - } - } -}; -//We want access to the constructor's prototype -(function() { - function F(){} - F.prototype = QUnit; - QUnit = new F(); - //Make F QUnit's constructor so that we can add to the prototype later - QUnit.constructor = F; -}()); - -// deprecated; still export them to window to provide clear error messages -// next step: remove entirely -QUnit.equals = function() { - QUnit.push(false, false, false, "QUnit.equals has been deprecated since 2009 (e88049a0), use QUnit.equal instead"); -}; -QUnit.same = function() { - QUnit.push(false, false, false, "QUnit.same has been deprecated since 2009 (e88049a0), use QUnit.deepEqual instead"); -}; - -// Maintain internal state -var config = { - // The queue of tests to run - queue: [], + // If a test is running, adjust its semaphore + config.current.semaphore -= count || 1; - // block until document ready - blocking: true, - - // when enabled, show only failing tests - // gets persisted through sessionStorage and can be changed in UI via checkbox - hidepassed: false, - - // by default, run previously failed tests first - // very useful in combination with "Hide passed tests" checked - reorder: true, - - // by default, modify document.title when suite is done - altertitle: true, - - urlConfig: ['noglobals', 'notrycatch'], - - //logging callback queues - begin: [], - done: [], - log: [], - testStart: [], - testDone: [], - moduleStart: [], - moduleDone: [] -}; - -// Load paramaters -(function() { - var location = window.location || { search: "", protocol: "file:" }, - params = location.search.slice( 1 ).split( "&" ), - length = params.length, - urlParams = {}, - current; - - if ( params[ 0 ] ) { - for ( var i = 0; i < length; i++ ) { - current = params[ i ].split( "=" ); - current[ 0 ] = decodeURIComponent( current[ 0 ] ); - // allow just a key to turn on a flag, e.g., test.html?noglobals - current[ 1 ] = current[ 1 ] ? decodeURIComponent( current[ 1 ] ) : true; - urlParams[ current[ 0 ] ] = current[ 1 ]; - } - } - - QUnit.urlParams = urlParams; - config.filter = urlParams.filter; - - // Figure out if we're running the tests from a server or not - QUnit.isLocal = location.protocol === 'file:'; -}()); - -// Expose the API as global variables, unless an 'exports' -// object exists, in that case we assume we're in CommonJS - export everything at the end -if ( typeof exports === "undefined" || typeof require === "undefined" ) { - extend(window, QUnit); - window.QUnit = QUnit; -} + // Don't start until equal number of stop-calls + if ( config.current.semaphore > 0 ) { + return; + } -// define these after exposing globals to keep them in these QUnit namespace only -extend(QUnit, { - config: config, + // throw an Error if start is called more often than stop + if ( config.current.semaphore < 0 ) { + config.current.semaphore = 0; - // Initialize the configuration options - init: function() { - extend(config, { - stats: { all: 0, bad: 0 }, - moduleStats: { all: 0, bad: 0 }, - started: +new Date(), - updateRate: 1000, - blocking: false, - autostart: true, - autorun: false, - filter: "", - queue: [], - semaphore: 0 - }); - - var qunit = id( "qunit" ); - if ( qunit ) { - qunit.innerHTML = - '<h1 id="qunit-header">' + escapeInnerText( document.title ) + '</h1>' + - '<h2 id="qunit-banner"></h2>' + - '<div id="qunit-testrunner-toolbar"></div>' + - '<h2 id="qunit-userAgent"></h2>' + - '<ol id="qunit-tests"></ol>'; + QUnit.pushFailure( + "Called start() while already started (test's semaphore was 0 already)", + sourceFromStacktrace( 2 ) + ); + return; + } } - var tests = id( "qunit-tests" ), - banner = id( "qunit-banner" ), - result = id( "qunit-testresult" ); + resumeProcessing(); + }, - if ( tests ) { - tests.innerHTML = ""; - } + // DEPRECATED: QUnit.stop() will be removed in QUnit 2.0. + stop: function( count ) { - if ( banner ) { - banner.className = ""; + // If there isn't a test running, don't allow QUnit.stop() to be called + if ( !config.current ) { + throw new Error( "Called stop() outside of a test context" ); } - if ( result ) { - result.parentNode.removeChild( result ); - } + // If a test is running, adjust its semaphore + config.current.semaphore += count || 1; - if ( tests ) { - result = document.createElement( "p" ); - result.id = "qunit-testresult"; - result.className = "result"; - tests.parentNode.insertBefore( result, tests ); - result.innerHTML = 'Running...<br/> '; - } + pauseProcessing(); }, - // Resets the test setup. Useful for tests that modify the DOM. - // If jQuery is available, uses jQuery's html(), otherwise just innerHTML. - reset: function() { - if ( window.jQuery ) { - jQuery( "#qunit-fixture" ).html( config.fixture ); - } else { - var main = id( 'qunit-fixture' ); - if ( main ) { - main.innerHTML = config.fixture; - } - } - }, - - // Trigger an event on an element. - // @example triggerEvent( document.body, "click" ); - triggerEvent: function( elem, type, event ) { - if ( document.createEvent ) { - event = document.createEvent("MouseEvents"); - event.initMouseEvent(type, true, true, elem.ownerDocument.defaultView, - 0, 0, 0, 0, 0, false, false, false, false, 0, null); - elem.dispatchEvent( event ); - - } else if ( elem.fireEvent ) { - elem.fireEvent("on"+type); - } - }, + config: config, // Safe object type checking is: function( type, obj ) { - return QUnit.objectType( obj ) == type; + return QUnit.objectType( obj ) === type; }, objectType: function( obj ) { - if (typeof obj === "undefined") { - return "undefined"; - - // consider: typeof null === object + if ( typeof obj === "undefined" ) { + return "undefined"; } - if (obj === null) { - return "null"; + + // Consider: typeof null === object + if ( obj === null ) { + return "null"; } - var type = toString.call( obj ).match(/^\[object\s(.*)\]$/)[1] || ''; + var match = toString.call( obj ).match( /^\[object\s(.*)\]$/ ), + type = match && match[ 1 ] || ""; - switch (type) { - case 'Number': - if (isNaN(obj)) { + switch ( type ) { + case "Number": + if ( isNaN( obj ) ) { return "nan"; } return "number"; - case 'String': - case 'Boolean': - case 'Array': - case 'Date': - case 'RegExp': - case 'Function': - return type.toLowerCase(); + case "String": + case "Boolean": + case "Array": + case "Date": + case "RegExp": + case "Function": + return type.toLowerCase(); } - if (typeof obj === "object") { - return "object"; + if ( typeof obj === "object" ) { + return "object"; } return undefined; }, - push: function(result, actual, expected, message) { - if (!config.current) { - throw new Error("assertion outside test context, was " + sourceFromStacktrace()); - } - var details = { - result: result, - message: message, - actual: actual, - expected: expected - }; - - message = escapeInnerText(message) || (result ? "okay" : "failed"); - message = '<span class="test-message">' + message + "</span>"; - var output = message; - if (!result) { - expected = escapeInnerText(QUnit.jsDump.parse(expected)); - actual = escapeInnerText(QUnit.jsDump.parse(actual)); - output += '<table><tr class="test-expected"><th>Expected: </th><td><pre>' + expected + '</pre></td></tr>'; - if (actual != expected) { - output += '<tr class="test-actual"><th>Result: </th><td><pre>' + actual + '</pre></td></tr>'; - output += '<tr class="test-diff"><th>Diff: </th><td><pre>' + QUnit.diff(expected, actual) +'</pre></td></tr>'; - } - var source = sourceFromStacktrace(); - if (source) { - details.source = source; - output += '<tr class="test-source"><th>Source: </th><td><pre>' + escapeInnerText(source) + '</pre></td></tr>'; - } - output += "</table>"; - } + extend: extend, - runLoggingCallbacks( 'log', QUnit, details ); + load: function() { + config.pageLoaded = true; - config.current.assertions.push({ - result: !!result, - message: output - }); - }, + // Initialize the configuration options + extend( config, { + stats: { all: 0, bad: 0 }, + moduleStats: { all: 0, bad: 0 }, + started: 0, + updateRate: 1000, + autostart: true, + filter: "" + }, true ); - pushFailure: function(message, source) { - var details = { - result: false, - message: message - }; - var output = escapeInnerText(message); - if (source) { - details.source = source; - output += '<table><tr class="test-source"><th>Source: </th><td><pre>' + escapeInnerText(source) + '</pre></td></tr></table>'; - } - runLoggingCallbacks( 'log', QUnit, details ); - config.current.assertions.push({ - result: false, - message: output - }); - }, + config.blocking = false; - url: function( params ) { - params = extend( extend( {}, QUnit.urlParams ), params ); - var querystring = "?", - key; - for ( key in params ) { - if ( !hasOwn.call( params, key ) ) { - continue; - } - querystring += encodeURIComponent( key ) + "=" + - encodeURIComponent( params[ key ] ) + "&"; + if ( config.autostart ) { + resumeProcessing(); } - return window.location.pathname + querystring.slice( 0, -1 ); - }, - - extend: extend, - id: id, - addEvent: addEvent -}); - -//QUnit.constructor is set to the empty F() above so that we can add to it's prototype later -//Doing this allows us to tell if the following methods have been overwritten on the actual -//QUnit object, which is a deprecated way of using the callbacks. -extend(QUnit.constructor.prototype, { - // Logging callbacks; all receive a single argument with the listed properties - // run test/logs.html for any related changes - begin: registerLoggingCallback('begin'), - // done: { failed, passed, total, runtime } - done: registerLoggingCallback('done'), - // log: { result, actual, expected, message } - log: registerLoggingCallback('log'), - // testStart: { name } - testStart: registerLoggingCallback('testStart'), - // testDone: { name, failed, passed, total } - testDone: registerLoggingCallback('testDone'), - // moduleStart: { name } - moduleStart: registerLoggingCallback('moduleStart'), - // moduleDone: { name, failed, passed, total } - moduleDone: registerLoggingCallback('moduleDone') + } }); -if ( typeof document === "undefined" || document.readyState === "complete" ) { - config.autorun = true; -} - -QUnit.load = function() { - runLoggingCallbacks( 'begin', QUnit, {} ); +// Register logging callbacks +(function() { + var i, l, key, + callbacks = [ "begin", "done", "log", "testStart", "testDone", + "moduleStart", "moduleDone" ]; + + function registerLoggingCallback( key ) { + var loggingCallback = function( callback ) { + if ( QUnit.objectType( callback ) !== "function" ) { + throw new Error( + "QUnit logging methods require a callback function as their first parameters." + ); + } - // Initialize the config, saving the execution queue - var oldconfig = extend({}, config); - QUnit.init(); - extend(config, oldconfig); + config.callbacks[ key ].push( callback ); + }; - config.blocking = false; + // DEPRECATED: This will be removed on QUnit 2.0.0+ + // Stores the registered functions allowing restoring + // at verifyLoggingCallbacks() if modified + loggingCallbacks[ key ] = loggingCallback; - var urlConfigHtml = '', len = config.urlConfig.length; - for ( var i = 0, val; i < len; i++ ) { - val = config.urlConfig[i]; - config[val] = QUnit.urlParams[val]; - urlConfigHtml += '<label><input name="' + val + '" type="checkbox"' + ( config[val] ? ' checked="checked"' : '' ) + '>' + val + '</label>'; + return loggingCallback; } - var userAgent = id("qunit-userAgent"); - if ( userAgent ) { - userAgent.innerHTML = navigator.userAgent; - } - var banner = id("qunit-header"); - if ( banner ) { - banner.innerHTML = '<a href="' + QUnit.url({ filter: undefined }) + '"> ' + banner.innerHTML + '</a> ' + urlConfigHtml; - addEvent( banner, "change", function( event ) { - var params = {}; - params[ event.target.name ] = event.target.checked ? true : undefined; - window.location = QUnit.url( params ); - }); - } + for ( i = 0, l = callbacks.length; i < l; i++ ) { + key = callbacks[ i ]; - var toolbar = id("qunit-testrunner-toolbar"); - if ( toolbar ) { - var filter = document.createElement("input"); - filter.type = "checkbox"; - filter.id = "qunit-filter-pass"; - addEvent( filter, "click", function() { - var ol = document.getElementById("qunit-tests"); - if ( filter.checked ) { - ol.className = ol.className + " hidepass"; - } else { - var tmp = " " + ol.className.replace( /[\n\t\r]/g, " " ) + " "; - ol.className = tmp.replace(/ hidepass /, " "); - } - if ( defined.sessionStorage ) { - if (filter.checked) { - sessionStorage.setItem("qunit-filter-passed-tests", "true"); - } else { - sessionStorage.removeItem("qunit-filter-passed-tests"); - } - } - }); - if ( config.hidepassed || defined.sessionStorage && sessionStorage.getItem("qunit-filter-passed-tests") ) { - filter.checked = true; - var ol = document.getElementById("qunit-tests"); - ol.className = ol.className + " hidepass"; + // Initialize key collection of logging callback + if ( QUnit.objectType( config.callbacks[ key ] ) === "undefined" ) { + config.callbacks[ key ] = []; } - toolbar.appendChild( filter ); - var label = document.createElement("label"); - label.setAttribute("for", "qunit-filter-pass"); - label.innerHTML = "Hide passed tests"; - toolbar.appendChild( label ); + QUnit[ key ] = registerLoggingCallback( key ); } - - var main = id('qunit-fixture'); - if ( main ) { - config.fixture = main.innerHTML; +})(); + +// `onErrorFnPrev` initialized at top of scope +// Preserve other handlers +onErrorFnPrev = window.onerror; + +// Cover uncaught exceptions +// Returning true will suppress the default browser handler, +// returning false will let it run. +window.onerror = function( error, filePath, linerNr ) { + var ret = false; + if ( onErrorFnPrev ) { + ret = onErrorFnPrev( error, filePath, linerNr ); } - if (config.autostart) { - QUnit.start(); + // Treat return value as window.onerror itself does, + // Only do our handling if not suppressed. + if ( ret !== true ) { + if ( QUnit.config.current ) { + if ( QUnit.config.current.ignoreGlobalErrors ) { + return true; + } + QUnit.pushFailure( error, filePath + ":" + linerNr ); + } else { + QUnit.test( "global failure", extend(function() { + QUnit.pushFailure( error, filePath + ":" + linerNr ); + }, { validTest: true } ) ); + } + return false; } -}; -addEvent(window, "load", QUnit.load); - -// addEvent(window, "error") gives us a useless event object -window.onerror = function( message, file, line ) { - if ( QUnit.config.current ) { - QUnit.pushFailure( message, file + ":" + line ); - } else { - QUnit.test( "global failure", function() { - QUnit.pushFailure( message, file + ":" + line ); - }); - } + return ret; }; function done() { + var runtime, passed; + config.autorun = true; // Log the last module results - if ( config.currentModule ) { - runLoggingCallbacks( 'moduleDone', QUnit, { - name: config.currentModule, + if ( config.previousModule ) { + runLoggingCallbacks( "moduleDone", { + name: config.previousModule.name, + tests: config.previousModule.tests, failed: config.moduleStats.bad, passed: config.moduleStats.all - config.moduleStats.bad, - total: config.moduleStats.all - } ); - } - - var banner = id("qunit-banner"), - tests = id("qunit-tests"), - runtime = +new Date() - config.started, - passed = config.stats.all - config.stats.bad, - html = [ - 'Tests completed in ', - runtime, - ' milliseconds.<br/>', - '<span class="passed">', - passed, - '</span> tests of <span class="total">', - config.stats.all, - '</span> passed, <span class="failed">', - config.stats.bad, - '</span> failed.' - ].join(''); - - if ( banner ) { - banner.className = (config.stats.bad ? "qunit-fail" : "qunit-pass"); - } - - if ( tests ) { - id( "qunit-testresult" ).innerHTML = html; - } - - if ( config.altertitle && typeof document !== "undefined" && document.title ) { - // show ✖ for good, ✔ for bad suite result in title - // use escape sequences in case file gets loaded with non-utf-8-charset - document.title = [ - (config.stats.bad ? "\u2716" : "\u2714"), - document.title.replace(/^[\u2714\u2716] /i, "") - ].join(" "); + total: config.moduleStats.all, + runtime: now() - config.moduleStats.started + }); } + delete config.previousModule; - // clear own sessionStorage items if all tests passed - if ( config.reorder && defined.sessionStorage && config.stats.bad === 0 ) { - var key; - for ( var i = 0; i < sessionStorage.length; i++ ) { - key = sessionStorage.key( i++ ); - if ( key.indexOf("qunit-test-") === 0 ) { - sessionStorage.removeItem( key ); - } - } - } + runtime = now() - config.started; + passed = config.stats.all - config.stats.bad; - runLoggingCallbacks( 'done', QUnit, { + runLoggingCallbacks( "done", { failed: config.stats.bad, passed: passed, total: config.stats.all, runtime: runtime - } ); -} - -function validTest( name ) { - var filter = config.filter, - run = false; - - if ( !filter ) { - return true; - } - - var not = filter.charAt( 0 ) === "!"; - if ( not ) { - filter = filter.slice( 1 ); - } - - if ( name.indexOf( filter ) !== -1 ) { - return !not; - } - - if ( not ) { - run = true; - } - - return run; + }); } -// so far supports only Firefox, Chrome and Opera (buggy), Safari (for real exceptions) -// Later Safari and IE10 are supposed to support error.stack as well +// Doesn't support IE6 to IE9, it will return undefined on these browsers // See also https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Error/Stack function extractStacktrace( e, offset ) { - offset = offset || 3; - if (e.stacktrace) { - // Opera - return e.stacktrace.split("\n")[offset + 3]; - } else if (e.stack) { - // Firefox, Chrome - var stack = e.stack.split("\n"); - if (/^error$/i.test(stack[0])) { + offset = offset === undefined ? 4 : offset; + + var stack, include, i; + + if ( e.stack ) { + stack = e.stack.split( "\n" ); + if ( /^error$/i.test( stack[ 0 ] ) ) { stack.shift(); } - return stack[offset]; - } else if (e.sourceURL) { - // Safari, PhantomJS - // hopefully one day Safari provides actual stacktraces + if ( fileName ) { + include = []; + for ( i = offset; i < stack.length; i++ ) { + if ( stack[ i ].indexOf( fileName ) !== -1 ) { + break; + } + include.push( stack[ i ] ); + } + if ( include.length ) { + return include.join( "\n" ); + } + } + return stack[ offset ]; + + // Support: Safari <=6 only + } else if ( e.sourceURL ) { + // exclude useless self-reference for generated Error objects if ( /qunit.js$/.test( e.sourceURL ) ) { return; } + // for actual exceptions, this is useful return e.sourceURL + ":" + e.line; } } -function sourceFromStacktrace(offset) { - try { - throw new Error(); - } catch ( e ) { - return extractStacktrace( e, offset ); - } -} -function escapeInnerText(s) { - if (!s) { - return ""; - } - s = s + ""; - return s.replace(/[\&<>]/g, function(s) { - switch(s) { - case "&": return "&"; - case "<": return "<"; - case ">": return ">"; - default: return s; +function sourceFromStacktrace( offset ) { + var error = new Error(); + + // Support: Safari <=7 only, IE <=10 - 11 only + // Not all browsers generate the `stack` property for `new Error()`, see also #636 + if ( !error.stack ) { + try { + throw error; + } catch ( err ) { + error = err; } - }); + } + + return extractStacktrace( error, offset ); } function synchronize( callback, last ) { + if ( QUnit.objectType( callback ) === "array" ) { + while ( callback.length ) { + synchronize( callback.shift() ); + } + return; + } config.queue.push( callback ); if ( config.autorun && !config.blocking ) { - process(last); + process( last ); } } @@ -982,14 +569,20 @@ function process( last ) { function next() { process( last ); } - var start = new Date().getTime(); - config.depth = config.depth ? config.depth + 1 : 1; + var start = now(); + config.depth = ( config.depth || 0 ) + 1; while ( config.queue.length && !config.blocking ) { - if ( !defined.setTimeout || config.updateRate <= 0 || ( ( new Date().getTime() - start ) < config.updateRate ) ) { + if ( !defined.setTimeout || config.updateRate <= 0 || + ( ( now() - start ) < config.updateRate ) ) { + if ( config.current ) { + + // Reset async tracking for each phase of the Test lifecycle + config.current.usedAsync = false; + } config.queue.shift()(); } else { - window.setTimeout( next, 13 ); + setTimeout( next, 13 ); break; } } @@ -999,41 +592,122 @@ function process( last ) { } } +function begin() { + var i, l, + modulesLog = []; + + // If the test run hasn't officially begun yet + if ( !config.started ) { + + // Record the time of the test run's beginning + config.started = now(); + + verifyLoggingCallbacks(); + + // Delete the loose unnamed module if unused. + if ( config.modules[ 0 ].name === "" && config.modules[ 0 ].tests.length === 0 ) { + config.modules.shift(); + } + + // Avoid unnecessary information by not logging modules' test environments + for ( i = 0, l = config.modules.length; i < l; i++ ) { + modulesLog.push({ + name: config.modules[ i ].name, + tests: config.modules[ i ].tests + }); + } + + // The test run is officially beginning now + runLoggingCallbacks( "begin", { + totalTests: Test.count, + modules: modulesLog + }); + } + + config.blocking = false; + process( true ); +} + +function resumeProcessing() { + runStarted = true; + + // A slight delay to allow this iteration of the event loop to finish (more assertions, etc.) + if ( defined.setTimeout ) { + setTimeout(function() { + if ( config.current && config.current.semaphore > 0 ) { + return; + } + if ( config.timeout ) { + clearTimeout( config.timeout ); + } + + begin(); + }, 13 ); + } else { + begin(); + } +} + +function pauseProcessing() { + config.blocking = true; + + if ( config.testTimeout && defined.setTimeout ) { + clearTimeout( config.timeout ); + config.timeout = setTimeout(function() { + if ( config.current ) { + config.current.semaphore = 0; + QUnit.pushFailure( "Test timed out", sourceFromStacktrace( 2 ) ); + } else { + throw new Error( "Test timed out" ); + } + resumeProcessing(); + }, config.testTimeout ); + } +} + function saveGlobal() { config.pollution = []; if ( config.noglobals ) { for ( var key in window ) { - if ( !hasOwn.call( window, key ) ) { - continue; + if ( hasOwn.call( window, key ) ) { + // in Opera sometimes DOM element ids show up here, ignore them + if ( /^qunit-test-output/.test( key ) ) { + continue; + } + config.pollution.push( key ); } - config.pollution.push( key ); } } } -function checkPollution( name ) { - var old = config.pollution; +function checkPollution() { + var newGlobals, + deletedGlobals, + old = config.pollution; + saveGlobal(); - var newGlobals = diff( config.pollution, old ); + newGlobals = diff( config.pollution, old ); if ( newGlobals.length > 0 ) { - QUnit.pushFailure( "Introduced global variable(s): " + newGlobals.join(", ") ); + QUnit.pushFailure( "Introduced global variable(s): " + newGlobals.join( ", " ) ); } - var deletedGlobals = diff( old, config.pollution ); + deletedGlobals = diff( old, config.pollution ); if ( deletedGlobals.length > 0 ) { - QUnit.pushFailure( "Deleted global variable(s): " + deletedGlobals.join(", ") ); + QUnit.pushFailure( "Deleted global variable(s): " + deletedGlobals.join( ", " ) ); } } // returns a new Array with the elements that are in a but not in b function diff( a, b ) { - var result = a.slice(); - for ( var i = 0; i < result.length; i++ ) { - for ( var j = 0; j < b.length; j++ ) { - if ( result[i] === b[j] ) { - result.splice(i, 1); + var i, j, + result = a.slice(); + + for ( i = 0; i < result.length; i++ ) { + for ( j = 0; j < b.length; j++ ) { + if ( result[ i ] === b[ j ] ) { + result.splice( i, 1 ); i--; break; } @@ -1042,628 +716,3113 @@ function diff( a, b ) { return result; } -function extend(a, b) { +function extend( a, b, undefOnly ) { for ( var prop in b ) { - if ( b[prop] === undefined ) { - delete a[prop]; - - // Avoid "Member not found" error in IE8 caused by setting window.constructor - } else if ( prop !== "constructor" || a !== window ) { - a[prop] = b[prop]; + if ( hasOwn.call( b, prop ) ) { + + // Avoid "Member not found" error in IE8 caused by messing with window.constructor + if ( !( prop === "constructor" && a === window ) ) { + if ( b[ prop ] === undefined ) { + delete a[ prop ]; + } else if ( !( undefOnly && typeof a[ prop ] !== "undefined" ) ) { + a[ prop ] = b[ prop ]; + } + } } } return a; } -function addEvent(elem, type, fn) { - if ( elem.addEventListener ) { - elem.addEventListener( type, fn, false ); - } else if ( elem.attachEvent ) { - elem.attachEvent( "on" + type, fn ); - } else { - fn(); +function runLoggingCallbacks( key, args ) { + var i, l, callbacks; + + callbacks = config.callbacks[ key ]; + for ( i = 0, l = callbacks.length; i < l; i++ ) { + callbacks[ i ]( args ); } } -function id(name) { - return !!(typeof document !== "undefined" && document && document.getElementById) && - document.getElementById( name ); +// DEPRECATED: This will be removed on 2.0.0+ +// This function verifies if the loggingCallbacks were modified by the user +// If so, it will restore it, assign the given callback and print a console warning +function verifyLoggingCallbacks() { + var loggingCallback, userCallback; + + for ( loggingCallback in loggingCallbacks ) { + if ( QUnit[ loggingCallback ] !== loggingCallbacks[ loggingCallback ] ) { + + userCallback = QUnit[ loggingCallback ]; + + // Restore the callback function + QUnit[ loggingCallback ] = loggingCallbacks[ loggingCallback ]; + + // Assign the deprecated given callback + QUnit[ loggingCallback ]( userCallback ); + + if ( window.console && window.console.warn ) { + window.console.warn( + "QUnit." + loggingCallback + " was replaced with a new value.\n" + + "Please, check out the documentation on how to apply logging callbacks.\n" + + "Reference: http://api.qunitjs.com/category/callbacks/" + ); + } + } + } } -function registerLoggingCallback(key){ - return function(callback){ - config[key].push( callback ); - }; +// from jquery.js +function inArray( elem, array ) { + if ( array.indexOf ) { + return array.indexOf( elem ); + } + + for ( var i = 0, length = array.length; i < length; i++ ) { + if ( array[ i ] === elem ) { + return i; + } + } + + return -1; } -// Supports deprecated method of completely overwriting logging callbacks -function runLoggingCallbacks(key, scope, args) { - //debugger; - var callbacks; - if ( QUnit.hasOwnProperty(key) ) { - QUnit[key].call(scope, args); +function Test( settings ) { + var i, l; + + ++Test.count; + + extend( this, settings ); + this.assertions = []; + this.semaphore = 0; + this.usedAsync = false; + this.module = config.currentModule; + this.stack = sourceFromStacktrace( 3 ); + + // Register unique strings + for ( i = 0, l = this.module.tests; i < l.length; i++ ) { + if ( this.module.tests[ i ].name === this.testName ) { + this.testName += " "; + } + } + + this.testId = generateHash( this.module.name, this.testName ); + + this.module.tests.push({ + name: this.testName, + testId: this.testId + }); + + if ( settings.skip ) { + + // Skipped tests will fully ignore any sent callback + this.callback = function() {}; + this.async = false; + this.expected = 0; } else { - callbacks = config[key]; - for( var i = 0; i < callbacks.length; i++ ) { - callbacks[i].call( scope, args ); + this.assert = new Assert( this ); + } +} + +Test.count = 0; + +Test.prototype = { + before: function() { + if ( + + // Emit moduleStart when we're switching from one module to another + this.module !== config.previousModule || + + // They could be equal (both undefined) but if the previousModule property doesn't + // yet exist it means this is the first test in a suite that isn't wrapped in a + // module, in which case we'll just emit a moduleStart event for 'undefined'. + // Without this, reporters can get testStart before moduleStart which is a problem. + !hasOwn.call( config, "previousModule" ) + ) { + if ( hasOwn.call( config, "previousModule" ) ) { + runLoggingCallbacks( "moduleDone", { + name: config.previousModule.name, + tests: config.previousModule.tests, + failed: config.moduleStats.bad, + passed: config.moduleStats.all - config.moduleStats.bad, + total: config.moduleStats.all, + runtime: now() - config.moduleStats.started + }); + } + config.previousModule = this.module; + config.moduleStats = { all: 0, bad: 0, started: now() }; + runLoggingCallbacks( "moduleStart", { + name: this.module.name, + tests: this.module.tests + }); } + + config.current = this; + + this.testEnvironment = extend( {}, this.module.testEnvironment ); + delete this.testEnvironment.beforeEach; + delete this.testEnvironment.afterEach; + + this.started = now(); + runLoggingCallbacks( "testStart", { + name: this.testName, + module: this.module.name, + testId: this.testId + }); + + if ( !config.pollution ) { + saveGlobal(); + } + }, + + run: function() { + var promise; + + config.current = this; + + if ( this.async ) { + QUnit.stop(); + } + + this.callbackStarted = now(); + + if ( config.notrycatch ) { + promise = this.callback.call( this.testEnvironment, this.assert ); + this.resolvePromise( promise ); + return; + } + + try { + promise = this.callback.call( this.testEnvironment, this.assert ); + this.resolvePromise( promise ); + } catch ( e ) { + this.pushFailure( "Died on test #" + ( this.assertions.length + 1 ) + " " + + this.stack + ": " + ( e.message || e ), extractStacktrace( e, 0 ) ); + + // else next test will carry the responsibility + saveGlobal(); + + // Restart the tests if they're blocking + if ( config.blocking ) { + QUnit.start(); + } + } + }, + + after: function() { + checkPollution(); + }, + + queueHook: function( hook, hookName ) { + var promise, + test = this; + return function runHook() { + config.current = test; + if ( config.notrycatch ) { + promise = hook.call( test.testEnvironment, test.assert ); + test.resolvePromise( promise, hookName ); + return; + } + try { + promise = hook.call( test.testEnvironment, test.assert ); + test.resolvePromise( promise, hookName ); + } catch ( error ) { + test.pushFailure( hookName + " failed on " + test.testName + ": " + + ( error.message || error ), extractStacktrace( error, 0 ) ); + } + }; + }, + + // Currently only used for module level hooks, can be used to add global level ones + hooks: function( handler ) { + var hooks = []; + + // Hooks are ignored on skipped tests + if ( this.skip ) { + return hooks; + } + + if ( this.module.testEnvironment && + QUnit.objectType( this.module.testEnvironment[ handler ] ) === "function" ) { + hooks.push( this.queueHook( this.module.testEnvironment[ handler ], handler ) ); + } + + return hooks; + }, + + finish: function() { + config.current = this; + if ( config.requireExpects && this.expected === null ) { + this.pushFailure( "Expected number of assertions to be defined, but expect() was " + + "not called.", this.stack ); + } else if ( this.expected !== null && this.expected !== this.assertions.length ) { + this.pushFailure( "Expected " + this.expected + " assertions, but " + + this.assertions.length + " were run", this.stack ); + } else if ( this.expected === null && !this.assertions.length ) { + this.pushFailure( "Expected at least one assertion, but none were run - call " + + "expect(0) to accept zero assertions.", this.stack ); + } + + var i, + bad = 0; + + this.runtime = now() - this.started; + config.stats.all += this.assertions.length; + config.moduleStats.all += this.assertions.length; + + for ( i = 0; i < this.assertions.length; i++ ) { + if ( !this.assertions[ i ].result ) { + bad++; + config.stats.bad++; + config.moduleStats.bad++; + } + } + + runLoggingCallbacks( "testDone", { + name: this.testName, + module: this.module.name, + skipped: !!this.skip, + failed: bad, + passed: this.assertions.length - bad, + total: this.assertions.length, + runtime: this.runtime, + + // HTML Reporter use + assertions: this.assertions, + testId: this.testId, + + // DEPRECATED: this property will be removed in 2.0.0, use runtime instead + duration: this.runtime + }); + + // QUnit.reset() is deprecated and will be replaced for a new + // fixture reset function on QUnit 2.0/2.1. + // It's still called here for backwards compatibility handling + QUnit.reset(); + + config.current = undefined; + }, + + queue: function() { + var bad, + test = this; + + if ( !this.valid() ) { + return; + } + + function run() { + + // each of these can by async + synchronize([ + function() { + test.before(); + }, + + test.hooks( "beforeEach" ), + + function() { + test.run(); + }, + + test.hooks( "afterEach" ).reverse(), + + function() { + test.after(); + }, + function() { + test.finish(); + } + ]); + } + + // `bad` initialized at top of scope + // defer when previous test run passed, if storage is available + bad = QUnit.config.reorder && defined.sessionStorage && + +sessionStorage.getItem( "qunit-test-" + this.module.name + "-" + this.testName ); + + if ( bad ) { + run(); + } else { + synchronize( run, true ); + } + }, + + push: function( result, actual, expected, message ) { + var source, + details = { + module: this.module.name, + name: this.testName, + result: result, + message: message, + actual: actual, + expected: expected, + testId: this.testId, + runtime: now() - this.started + }; + + if ( !result ) { + source = sourceFromStacktrace(); + + if ( source ) { + details.source = source; + } + } + + runLoggingCallbacks( "log", details ); + + this.assertions.push({ + result: !!result, + message: message + }); + }, + + pushFailure: function( message, source, actual ) { + if ( !this instanceof Test ) { + throw new Error( "pushFailure() assertion outside test context, was " + + sourceFromStacktrace( 2 ) ); + } + + var details = { + module: this.module.name, + name: this.testName, + result: false, + message: message || "error", + actual: actual || null, + testId: this.testId, + runtime: now() - this.started + }; + + if ( source ) { + details.source = source; + } + + runLoggingCallbacks( "log", details ); + + this.assertions.push({ + result: false, + message: message + }); + }, + + resolvePromise: function( promise, phase ) { + var then, message, + test = this; + if ( promise != null ) { + then = promise.then; + if ( QUnit.objectType( then ) === "function" ) { + QUnit.stop(); + then.call( + promise, + QUnit.start, + function( error ) { + message = "Promise rejected " + + ( !phase ? "during" : phase.replace( /Each$/, "" ) ) + + " " + test.testName + ": " + ( error.message || error ); + test.pushFailure( message, extractStacktrace( error, 0 ) ); + + // else next test will carry the responsibility + saveGlobal(); + + // Unblock + QUnit.start(); + } + ); + } + } + }, + + valid: function() { + var include, + filter = config.filter && config.filter.toLowerCase(), + module = QUnit.urlParams.module && QUnit.urlParams.module.toLowerCase(), + fullName = ( this.module.name + ": " + this.testName ).toLowerCase(); + + // Internally-generated tests are always valid + if ( this.callback && this.callback.validTest ) { + return true; + } + + if ( config.testId.length > 0 && inArray( this.testId, config.testId ) < 0 ) { + return false; + } + + if ( module && ( !this.module.name || this.module.name.toLowerCase() !== module ) ) { + return false; + } + + if ( !filter ) { + return true; + } + + include = filter.charAt( 0 ) !== "!"; + if ( !include ) { + filter = filter.slice( 1 ); + } + + // If the filter matches, we need to honour include + if ( fullName.indexOf( filter ) !== -1 ) { + return include; + } + + // Otherwise, do the opposite + return !include; + } + +}; + +// Resets the test setup. Useful for tests that modify the DOM. +/* +DEPRECATED: Use multiple tests instead of resetting inside a test. +Use testStart or testDone for custom cleanup. +This method will throw an error in 2.0, and will be removed in 2.1 +*/ +QUnit.reset = function() { + + // Return on non-browser environments + // This is necessary to not break on node tests + if ( typeof window === "undefined" ) { + return; + } + + var fixture = defined.document && document.getElementById && + document.getElementById( "qunit-fixture" ); + + if ( fixture ) { + fixture.innerHTML = config.fixture; + } +}; + +QUnit.pushFailure = function() { + if ( !QUnit.config.current ) { + throw new Error( "pushFailure() assertion outside test context, in " + + sourceFromStacktrace( 2 ) ); + } + + // Gets current test obj + var currentTest = QUnit.config.current; + + return currentTest.pushFailure.apply( currentTest, arguments ); +}; + +// Based on Java's String.hashCode, a simple but not +// rigorously collision resistant hashing function +function generateHash( module, testName ) { + var hex, + i = 0, + hash = 0, + str = module + "\x1C" + testName, + len = str.length; + + for ( ; i < len; i++ ) { + hash = ( ( hash << 5 ) - hash ) + str.charCodeAt( i ); + hash |= 0; } + + // Convert the possibly negative integer hash code into an 8 character hex string, which isn't + // strictly necessary but increases user understanding that the id is a SHA-like hash + hex = ( 0x100000000 + hash ).toString( 16 ); + if ( hex.length < 8 ) { + hex = "0000000" + hex; + } + + return hex.slice( -8 ); } -// Test for equality any JavaScript type. -// Author: Philippe Rathé <prathe@gmail.com> -QUnit.equiv = (function() { +function Assert( testContext ) { + this.test = testContext; +} - var innerEquiv; // the real equiv function - var callers = []; // stack to decide between skip/abort functions - var parents = []; // stack to avoiding loops from circular referencing +// Assert helpers +QUnit.assert = Assert.prototype = { - // Call the o related callback with the given arguments. - function bindCallbacks(o, callbacks, args) { - var prop = QUnit.objectType(o); - if (prop) { - if (QUnit.objectType(callbacks[prop]) === "function") { - return callbacks[prop].apply(callbacks, args); + // Specify the number of expected assertions to guarantee that failed test + // (no assertions are run at all) don't slip through. + expect: function( asserts ) { + if ( arguments.length === 1 ) { + this.test.expected = asserts; + } else { + return this.test.expected; + } + }, + + // Increment this Test's semaphore counter, then return a single-use function that + // decrements that counter a maximum of once. + async: function() { + var test = this.test, + popped = false; + + test.semaphore += 1; + test.usedAsync = true; + pauseProcessing(); + + return function done() { + if ( !popped ) { + test.semaphore -= 1; + popped = true; + resumeProcessing(); } else { - return callbacks[prop]; // or undefined + test.pushFailure( "Called the callback returned from `assert.async` more than once", + sourceFromStacktrace( 2 ) ); } + }; + }, + + // Exports test.push() to the user API + push: function( /* result, actual, expected, message */ ) { + var assert = this, + currentTest = ( assert instanceof Assert && assert.test ) || QUnit.config.current; + + // Backwards compatibility fix. + // Allows the direct use of global exported assertions and QUnit.assert.* + // Although, it's use is not recommended as it can leak assertions + // to other tests from async tests, because we only get a reference to the current test, + // not exactly the test where assertion were intended to be called. + if ( !currentTest ) { + throw new Error( "assertion outside test context, in " + sourceFromStacktrace( 2 ) ); + } + + if ( currentTest.usedAsync === true && currentTest.semaphore === 0 ) { + currentTest.pushFailure( "Assertion after the final `assert.async` was resolved", + sourceFromStacktrace( 2 ) ); + + // Allow this assertion to continue running anyway... + } + + if ( !( assert instanceof Assert ) ) { + assert = currentTest.assert; + } + return assert.test.push.apply( assert.test, arguments ); + }, + + ok: function( result, message ) { + message = message || ( result ? "okay" : "failed, expected argument to be truthy, was: " + + QUnit.dump.parse( result ) ); + this.push( !!result, result, true, message ); + }, + + notOk: function( result, message ) { + message = message || ( !result ? "okay" : "failed, expected argument to be falsy, was: " + + QUnit.dump.parse( result ) ); + this.push( !result, result, false, message ); + }, + + equal: function( actual, expected, message ) { + /*jshint eqeqeq:false */ + this.push( expected == actual, actual, expected, message ); + }, + + notEqual: function( actual, expected, message ) { + /*jshint eqeqeq:false */ + this.push( expected != actual, actual, expected, message ); + }, + + propEqual: function( actual, expected, message ) { + actual = objectValues( actual ); + expected = objectValues( expected ); + this.push( QUnit.equiv( actual, expected ), actual, expected, message ); + }, + + notPropEqual: function( actual, expected, message ) { + actual = objectValues( actual ); + expected = objectValues( expected ); + this.push( !QUnit.equiv( actual, expected ), actual, expected, message ); + }, + + deepEqual: function( actual, expected, message ) { + this.push( QUnit.equiv( actual, expected ), actual, expected, message ); + }, + + notDeepEqual: function( actual, expected, message ) { + this.push( !QUnit.equiv( actual, expected ), actual, expected, message ); + }, + + strictEqual: function( actual, expected, message ) { + this.push( expected === actual, actual, expected, message ); + }, + + notStrictEqual: function( actual, expected, message ) { + this.push( expected !== actual, actual, expected, message ); + }, + + "throws": function( block, expected, message ) { + var actual, expectedType, + expectedOutput = expected, + ok = false, + currentTest = ( this instanceof Assert && this.test ) || QUnit.config.current; + + // 'expected' is optional unless doing string comparison + if ( message == null && typeof expected === "string" ) { + message = expected; + expected = null; } + + currentTest.ignoreGlobalErrors = true; + try { + block.call( currentTest.testEnvironment ); + } catch (e) { + actual = e; + } + currentTest.ignoreGlobalErrors = false; + + if ( actual ) { + expectedType = QUnit.objectType( expected ); + + // we don't want to validate thrown error + if ( !expected ) { + ok = true; + expectedOutput = null; + + // expected is a regexp + } else if ( expectedType === "regexp" ) { + ok = expected.test( errorString( actual ) ); + + // expected is a string + } else if ( expectedType === "string" ) { + ok = expected === errorString( actual ); + + // expected is a constructor, maybe an Error constructor + } else if ( expectedType === "function" && actual instanceof expected ) { + ok = true; + + // expected is an Error object + } else if ( expectedType === "object" ) { + ok = actual instanceof expected.constructor && + actual.name === expected.name && + actual.message === expected.message; + + // expected is a validation function which returns true if validation passed + } else if ( expectedType === "function" && expected.call( {}, actual ) === true ) { + expectedOutput = null; + ok = true; + } + } + + currentTest.assert.push( ok, actual, expectedOutput, message ); } +}; - var getProto = Object.getPrototypeOf || function (obj) { - return obj.__proto__; - }; +// Provide an alternative to assert.throws(), for enviroments that consider throws a reserved word +// Known to us are: Closure Compiler, Narwhal +(function() { + /*jshint sub:true */ + Assert.prototype.raises = Assert.prototype[ "throws" ]; +}()); - var callbacks = (function () { +// Test for equality any JavaScript type. +// Author: Philippe Rathé <prathe@gmail.com> +QUnit.equiv = (function() { - // for string, boolean, number and null - function useStrictEquality(b, a) { - if (b instanceof a.constructor || a instanceof b.constructor) { - // to catch short annotaion VS 'new' annotation of a - // declaration - // e.g. var i = 1; - // var j = new Number(1); - return a == b; + // Call the o related callback with the given arguments. + function bindCallbacks( o, callbacks, args ) { + var prop = QUnit.objectType( o ); + if ( prop ) { + if ( QUnit.objectType( callbacks[ prop ] ) === "function" ) { + return callbacks[ prop ].apply( callbacks, args ); } else { - return a === b; + return callbacks[ prop ]; // or undefined } } + } - return { - "string" : useStrictEquality, - "boolean" : useStrictEquality, - "number" : useStrictEquality, - "null" : useStrictEquality, - "undefined" : useStrictEquality, + // the real equiv function + var innerEquiv, - "nan" : function(b) { - return isNaN(b); - }, + // stack to decide between skip/abort functions + callers = [], - "date" : function(b, a) { - return QUnit.objectType(b) === "date" && a.valueOf() === b.valueOf(); - }, - - "regexp" : function(b, a) { - return QUnit.objectType(b) === "regexp" && - // the regex itself - a.source === b.source && - // and its modifers - a.global === b.global && - // (gmi) ... - a.ignoreCase === b.ignoreCase && - a.multiline === b.multiline; - }, + // stack to avoiding loops from circular referencing + parents = [], + parentsB = [], - // - skip when the property is a method of an instance (OOP) - // - abort otherwise, - // initial === would have catch identical references anyway - "function" : function() { - var caller = callers[callers.length - 1]; - return caller !== Object && typeof caller !== "undefined"; - }, + getProto = Object.getPrototypeOf || function( obj ) { + /* jshint camelcase: false, proto: true */ + return obj.__proto__; + }, + callbacks = (function() { - "array" : function(b, a) { - var i, j, loop; - var len; + // for string, boolean, number and null + function useStrictEquality( b, a ) { - // b could be an object literal here - if (QUnit.objectType(b) !== "array") { - return false; - } + /*jshint eqeqeq:false */ + if ( b instanceof a.constructor || a instanceof b.constructor ) { - len = a.length; - if (len !== b.length) { // safe and faster - return false; + // to catch short annotation VS 'new' annotation of a + // declaration + // e.g. var i = 1; + // var j = new Number(1); + return a == b; + } else { + return a === b; } + } - // track reference to avoid circular references - parents.push(a); - for (i = 0; i < len; i++) { - loop = false; - for (j = 0; j < parents.length; j++) { - if (parents[j] === a[i]) { - loop = true;// dont rewalk array - } - } - if (!loop && !innerEquiv(a[i], b[i])) { - parents.pop(); + return { + "string": useStrictEquality, + "boolean": useStrictEquality, + "number": useStrictEquality, + "null": useStrictEquality, + "undefined": useStrictEquality, + + "nan": function( b ) { + return isNaN( b ); + }, + + "date": function( b, a ) { + return QUnit.objectType( b ) === "date" && a.valueOf() === b.valueOf(); + }, + + "regexp": function( b, a ) { + return QUnit.objectType( b ) === "regexp" && + + // the regex itself + a.source === b.source && + + // and its modifiers + a.global === b.global && + + // (gmi) ... + a.ignoreCase === b.ignoreCase && + a.multiline === b.multiline && + a.sticky === b.sticky; + }, + + // - skip when the property is a method of an instance (OOP) + // - abort otherwise, + // initial === would have catch identical references anyway + "function": function() { + var caller = callers[ callers.length - 1 ]; + return caller !== Object && typeof caller !== "undefined"; + }, + + "array": function( b, a ) { + var i, j, len, loop, aCircular, bCircular; + + // b could be an object literal here + if ( QUnit.objectType( b ) !== "array" ) { return false; } - } - parents.pop(); - return true; - }, - "object" : function(b, a) { - var i, j, loop; - var eq = true; // unless we can proove it - var aProperties = [], bProperties = []; // collection of - // strings - - // comparing constructors is more strict than using - // instanceof - if (a.constructor !== b.constructor) { - // Allow objects with no prototype to be equivalent to - // objects with Object as their constructor. - if (!((getProto(a) === null && getProto(b) === Object.prototype) || - (getProto(b) === null && getProto(a) === Object.prototype))) - { + len = a.length; + if ( len !== b.length ) { + // safe and faster return false; } - } - // stack constructor before traversing properties - callers.push(a.constructor); - // track reference to avoid circular references - parents.push(a); - - for (i in a) { // be strict: don't ensures hasOwnProperty - // and go deep - loop = false; - for (j = 0; j < parents.length; j++) { - if (parents[j] === a[i]) { - // don't go down the same path twice - loop = true; + // track reference to avoid circular references + parents.push( a ); + parentsB.push( b ); + for ( i = 0; i < len; i++ ) { + loop = false; + for ( j = 0; j < parents.length; j++ ) { + aCircular = parents[ j ] === a[ i ]; + bCircular = parentsB[ j ] === b[ i ]; + if ( aCircular || bCircular ) { + if ( a[ i ] === b[ i ] || aCircular && bCircular ) { + loop = true; + } else { + parents.pop(); + parentsB.pop(); + return false; + } + } + } + if ( !loop && !innerEquiv( a[ i ], b[ i ] ) ) { + parents.pop(); + parentsB.pop(); + return false; + } + } + parents.pop(); + parentsB.pop(); + return true; + }, + + "object": function( b, a ) { + + /*jshint forin:false */ + var i, j, loop, aCircular, bCircular, + // Default to true + eq = true, + aProperties = [], + bProperties = []; + + // comparing constructors is more strict than using + // instanceof + if ( a.constructor !== b.constructor ) { + + // Allow objects with no prototype to be equivalent to + // objects with Object as their constructor. + if ( !( ( getProto( a ) === null && getProto( b ) === Object.prototype ) || + ( getProto( b ) === null && getProto( a ) === Object.prototype ) ) ) { + return false; } } - aProperties.push(i); // collect a's properties - if (!loop && !innerEquiv(a[i], b[i])) { - eq = false; - break; + // stack constructor before traversing properties + callers.push( a.constructor ); + + // track reference to avoid circular references + parents.push( a ); + parentsB.push( b ); + + // be strict: don't ensure hasOwnProperty and go deep + for ( i in a ) { + loop = false; + for ( j = 0; j < parents.length; j++ ) { + aCircular = parents[ j ] === a[ i ]; + bCircular = parentsB[ j ] === b[ i ]; + if ( aCircular || bCircular ) { + if ( a[ i ] === b[ i ] || aCircular && bCircular ) { + loop = true; + } else { + eq = false; + break; + } + } + } + aProperties.push( i ); + if ( !loop && !innerEquiv( a[ i ], b[ i ] ) ) { + eq = false; + break; + } } - } - callers.pop(); // unstack, we are done - parents.pop(); + parents.pop(); + parentsB.pop(); + callers.pop(); // unstack, we are done - for (i in b) { - bProperties.push(i); // collect b's properties - } + for ( i in b ) { + bProperties.push( i ); // collect b's properties + } - // Ensures identical properties name - return eq && innerEquiv(aProperties.sort(), bProperties.sort()); - } - }; - }()); + // Ensures identical properties name + return eq && innerEquiv( aProperties.sort(), bProperties.sort() ); + } + }; + }()); innerEquiv = function() { // can take multiple arguments - var args = Array.prototype.slice.apply(arguments); - if (args.length < 2) { + var args = [].slice.apply( arguments ); + if ( args.length < 2 ) { return true; // end transition } - return (function(a, b) { - if (a === b) { + return ( (function( a, b ) { + if ( a === b ) { return true; // catch the most you can - } else if (a === null || b === null || typeof a === "undefined" || + } else if ( a === null || b === null || typeof a === "undefined" || typeof b === "undefined" || - QUnit.objectType(a) !== QUnit.objectType(b)) { - return false; // don't lose time with error prone cases + QUnit.objectType( a ) !== QUnit.objectType( b ) ) { + + // don't lose time with error prone cases + return false; } else { - return bindCallbacks(a, callbacks, [ b, a ]); + return bindCallbacks( a, callbacks, [ b, a ] ); } // apply transition with (1..n) arguments - }(args[0], args[1]) && arguments.callee.apply(this, args.splice(1, args.length - 1))); + }( args[ 0 ], args[ 1 ] ) ) && + innerEquiv.apply( this, args.splice( 1, args.length - 1 ) ) ); }; return innerEquiv; - }()); -/** - * jsDump Copyright (c) 2008 Ariel Flesler - aflesler(at)gmail(dot)com | - * http://flesler.blogspot.com Licensed under BSD - * (http://www.opensource.org/licenses/bsd-license.php) Date: 5/15/2008 - * - * @projectDescription Advanced and extensible data dumping for Javascript. - * @version 1.0.0 - * @author Ariel Flesler - * @link {http://flesler.blogspot.com/2008/05/jsdump-pretty-dump-of-any-javascript.html} - */ -QUnit.jsDump = (function() { +// Based on jsDump by Ariel Flesler +// http://flesler.blogspot.com/2008/05/jsdump-pretty-dump-of-any-javascript.html +QUnit.dump = (function() { function quote( str ) { - return '"' + str.toString().replace(/"/g, '\\"') + '"'; + return "\"" + str.toString().replace( /"/g, "\\\"" ) + "\""; } function literal( o ) { - return o + ''; + return o + ""; } function join( pre, arr, post ) { - var s = jsDump.separator(), - base = jsDump.indent(), - inner = jsDump.indent(1); + var s = dump.separator(), + base = dump.indent(), + inner = dump.indent( 1 ); if ( arr.join ) { - arr = arr.join( ',' + s + inner ); + arr = arr.join( "," + s + inner ); } if ( !arr ) { return pre + post; } - return [ pre, inner + arr, base + post ].join(s); + return [ pre, inner + arr, base + post ].join( s ); } function array( arr, stack ) { - var i = arr.length, ret = new Array(i); + var i = arr.length, + ret = new Array( i ); + + if ( dump.maxDepth && dump.depth > dump.maxDepth ) { + return "[object Array]"; + } + this.up(); while ( i-- ) { - ret[i] = this.parse( arr[i] , undefined , stack); + ret[ i ] = this.parse( arr[ i ], undefined, stack ); } this.down(); - return join( '[', ret, ']' ); + return join( "[", ret, "]" ); } - var reName = /^function (\w+)/; + var reName = /^function (\w+)/, + dump = { - var jsDump = { - parse: function( obj, type, stack ) { //type is used mostly internally, you can fix a (custom)type in advance - stack = stack || [ ]; - var parser = this.parsers[ type || this.typeOf(obj) ]; - type = typeof parser; - var inStack = inArray(obj, stack); - if (inStack != -1) { - return 'recursion('+(inStack - stack.length)+')'; - } - //else - if (type == 'function') { - stack.push(obj); - var res = parser.call( this, obj, stack ); + // objType is used mostly internally, you can fix a (custom) type in advance + parse: function( obj, objType, stack ) { + stack = stack || []; + var res, parser, parserType, + inStack = inArray( obj, stack ); + + if ( inStack !== -1 ) { + return "recursion(" + ( inStack - stack.length ) + ")"; + } + + objType = objType || this.typeOf( obj ); + parser = this.parsers[ objType ]; + parserType = typeof parser; + + if ( parserType === "function" ) { + stack.push( obj ); + res = parser.call( this, obj, stack ); stack.pop(); return res; - } - // else - return (type == 'string') ? parser : this.parsers.error; - }, - typeOf: function( obj ) { - var type; - if ( obj === null ) { - type = "null"; - } else if (typeof obj === "undefined") { - type = "undefined"; - } else if (QUnit.is("RegExp", obj)) { - type = "regexp"; - } else if (QUnit.is("Date", obj)) { - type = "date"; - } else if (QUnit.is("Function", obj)) { - type = "function"; - } else if (typeof obj.setInterval !== undefined && typeof obj.document !== "undefined" && typeof obj.nodeType === "undefined") { - type = "window"; - } else if (obj.nodeType === 9) { - type = "document"; - } else if (obj.nodeType) { - type = "node"; - } else if ( - // native arrays - toString.call( obj ) === "[object Array]" || - // NodeList objects - ( typeof obj.length === "number" && typeof obj.item !== "undefined" && ( obj.length ? obj.item(0) === obj[0] : ( obj.item( 0 ) === null && typeof obj[0] === "undefined" ) ) ) - ) { - type = "array"; - } else { - type = typeof obj; - } - return type; - }, - separator: function() { - return this.multiline ? this.HTML ? '<br />' : '\n' : this.HTML ? ' ' : ' '; - }, - indent: function( extra ) {// extra can be a number, shortcut for increasing-calling-decreasing - if ( !this.multiline ) { - return ''; - } - var chr = this.indentChar; - if ( this.HTML ) { - chr = chr.replace(/\t/g,' ').replace(/ /g,' '); - } - return new Array( this._depth_ + (extra||0) ).join(chr); - }, - up: function( a ) { - this._depth_ += a || 1; - }, - down: function( a ) { - this._depth_ -= a || 1; - }, - setParser: function( name, parser ) { - this.parsers[name] = parser; - }, - // The next 3 are exposed so you can use them - quote: quote, - literal: literal, - join: join, - // - _depth_: 1, - // This is the list of parsers, to modify them, use jsDump.setParser - parsers: { - window: '[Window]', - document: '[Document]', - error: '[ERROR]', //when no parser is found, shouldn't happen - unknown: '[Unknown]', - 'null': 'null', - 'undefined': 'undefined', - 'function': function( fn ) { - var ret = 'function', - name = 'name' in fn ? fn.name : (reName.exec(fn)||[])[1];//functions never have name in IE - if ( name ) { - ret += ' ' + name; } - ret += '('; - - ret = [ ret, QUnit.jsDump.parse( fn, 'functionArgs' ), '){'].join(''); - return join( ret, QUnit.jsDump.parse(fn,'functionCode'), '}' ); + return ( parserType === "string" ) ? parser : this.parsers.error; }, - array: array, - nodelist: array, - 'arguments': array, - object: function( map, stack ) { - var ret = [ ], keys, key, val, i; - QUnit.jsDump.up(); - if (Object.keys) { - keys = Object.keys( map ); + typeOf: function( obj ) { + var type; + if ( obj === null ) { + type = "null"; + } else if ( typeof obj === "undefined" ) { + type = "undefined"; + } else if ( QUnit.is( "regexp", obj ) ) { + type = "regexp"; + } else if ( QUnit.is( "date", obj ) ) { + type = "date"; + } else if ( QUnit.is( "function", obj ) ) { + type = "function"; + } else if ( obj.setInterval !== undefined && + obj.document !== undefined && + obj.nodeType === undefined ) { + type = "window"; + } else if ( obj.nodeType === 9 ) { + type = "document"; + } else if ( obj.nodeType ) { + type = "node"; + } else if ( + + // native arrays + toString.call( obj ) === "[object Array]" || + + // NodeList objects + ( typeof obj.length === "number" && obj.item !== undefined && + ( obj.length ? obj.item( 0 ) === obj[ 0 ] : ( obj.item( 0 ) === null && + obj[ 0 ] === undefined ) ) ) + ) { + type = "array"; + } else if ( obj.constructor === Error.prototype.constructor ) { + type = "error"; } else { - keys = []; - for (key in map) { keys.push( key ); } + type = typeof obj; + } + return type; + }, + separator: function() { + return this.multiline ? this.HTML ? "<br />" : "\n" : this.HTML ? " " : " "; + }, + // extra can be a number, shortcut for increasing-calling-decreasing + indent: function( extra ) { + if ( !this.multiline ) { + return ""; } - keys.sort(); - for (i = 0; i < keys.length; i++) { - key = keys[ i ]; - val = map[ key ]; - ret.push( QUnit.jsDump.parse( key, 'key' ) + ': ' + QUnit.jsDump.parse( val, undefined, stack ) ); + var chr = this.indentChar; + if ( this.HTML ) { + chr = chr.replace( /\t/g, " " ).replace( / /g, " " ); } - QUnit.jsDump.down(); - return join( '{', ret, '}' ); + return new Array( this.depth + ( extra || 0 ) ).join( chr ); }, - node: function( node ) { - var open = QUnit.jsDump.HTML ? '<' : '<', - close = QUnit.jsDump.HTML ? '>' : '>'; + up: function( a ) { + this.depth += a || 1; + }, + down: function( a ) { + this.depth -= a || 1; + }, + setParser: function( name, parser ) { + this.parsers[ name ] = parser; + }, + // The next 3 are exposed so you can use them + quote: quote, + literal: literal, + join: join, + // + depth: 1, + maxDepth: QUnit.config.maxDepth, + + // This is the list of parsers, to modify them, use dump.setParser + parsers: { + window: "[Window]", + document: "[Document]", + error: function( error ) { + return "Error(\"" + error.message + "\")"; + }, + unknown: "[Unknown]", + "null": "null", + "undefined": "undefined", + "function": function( fn ) { + var ret = "function", + + // functions never have name in IE + name = "name" in fn ? fn.name : ( reName.exec( fn ) || [] )[ 1 ]; + + if ( name ) { + ret += " " + name; + } + ret += "( "; + + ret = [ ret, dump.parse( fn, "functionArgs" ), "){" ].join( "" ); + return join( ret, dump.parse( fn, "functionCode" ), "}" ); + }, + array: array, + nodelist: array, + "arguments": array, + object: function( map, stack ) { + var keys, key, val, i, nonEnumerableProperties, + ret = []; + + if ( dump.maxDepth && dump.depth > dump.maxDepth ) { + return "[object Object]"; + } - var tag = node.nodeName.toLowerCase(), - ret = open + tag; + dump.up(); + keys = []; + for ( key in map ) { + keys.push( key ); + } - for ( var a in QUnit.jsDump.DOMAttrs ) { - var val = node[QUnit.jsDump.DOMAttrs[a]]; - if ( val ) { - ret += ' ' + a + '=' + QUnit.jsDump.parse( val, 'attribute' ); + // Some properties are not always enumerable on Error objects. + nonEnumerableProperties = [ "message", "name" ]; + for ( i in nonEnumerableProperties ) { + key = nonEnumerableProperties[ i ]; + if ( key in map && inArray( key, keys ) < 0 ) { + keys.push( key ); + } } - } - return ret + close + open + '/' + tag + close; - }, - functionArgs: function( fn ) {//function calls it internally, it's the arguments part of the function - var l = fn.length; - if ( !l ) { - return ''; - } + keys.sort(); + for ( i = 0; i < keys.length; i++ ) { + key = keys[ i ]; + val = map[ key ]; + ret.push( dump.parse( key, "key" ) + ": " + + dump.parse( val, undefined, stack ) ); + } + dump.down(); + return join( "{", ret, "}" ); + }, + node: function( node ) { + var len, i, val, + open = dump.HTML ? "<" : "<", + close = dump.HTML ? ">" : ">", + tag = node.nodeName.toLowerCase(), + ret = open + tag, + attrs = node.attributes; + + if ( attrs ) { + for ( i = 0, len = attrs.length; i < len; i++ ) { + val = attrs[ i ].nodeValue; + + // IE6 includes all attributes in .attributes, even ones not explicitly + // set. Those have values like undefined, null, 0, false, "" or + // "inherit". + if ( val && val !== "inherit" ) { + ret += " " + attrs[ i ].nodeName + "=" + + dump.parse( val, "attribute" ); + } + } + } + ret += close; - var args = new Array(l); - while ( l-- ) { - args[l] = String.fromCharCode(97+l);//97 is 'a' - } - return ' ' + args.join(', ') + ' '; - }, - key: quote, //object calls it internally, the key part of an item in a map - functionCode: '[code]', //function calls it internally, it's the content of the function - attribute: quote, //node calls it internally, it's an html attribute value - string: quote, - date: quote, - regexp: literal, //regex - number: literal, - 'boolean': literal - }, - DOMAttrs:{//attributes to dump from nodes, name=>realName - id:'id', - name:'name', - 'class':'className' - }, - HTML:false,//if true, entities are escaped ( <, >, \t, space and \n ) - indentChar:' ',//indentation unit - multiline:true //if true, items in a collection, are separated by a \n, else just a space. - }; + // Show content of TextNode or CDATASection + if ( node.nodeType === 3 || node.nodeType === 4 ) { + ret += node.nodeValue; + } - return jsDump; -}()); + return ret + open + "/" + tag + close; + }, -// from Sizzle.js -function getText( elems ) { - var ret = "", elem; + // function calls it internally, it's the arguments part of the function + functionArgs: function( fn ) { + var args, + l = fn.length; - for ( var i = 0; elems[i]; i++ ) { - elem = elems[i]; + if ( !l ) { + return ""; + } - // Get the text from text nodes and CDATA nodes - if ( elem.nodeType === 3 || elem.nodeType === 4 ) { - ret += elem.nodeValue; + args = new Array( l ); + while ( l-- ) { - // Traverse everything else, except comment nodes - } else if ( elem.nodeType !== 8 ) { - ret += getText( elem.childNodes ); - } - } + // 97 is 'a' + args[ l ] = String.fromCharCode( 97 + l ); + } + return " " + args.join( ", " ) + " "; + }, + // object calls it internally, the key part of an item in a map + key: quote, + // function calls it internally, it's the content of the function + functionCode: "[code]", + // node calls it internally, it's an html attribute value + attribute: quote, + string: quote, + date: quote, + regexp: literal, + number: literal, + "boolean": literal + }, + // if true, entities are escaped ( <, >, \t, space and \n ) + HTML: false, + // indentation unit + indentChar: " ", + // if true, items in a collection, are separated by a \n, else just a space. + multiline: true + }; - return ret; + return dump; +}()); + +// back compat +QUnit.jsDump = QUnit.dump; + +// For browser, export only select globals +if ( typeof window !== "undefined" ) { + + // Deprecated + // Extend assert methods to QUnit and Global scope through Backwards compatibility + (function() { + var i, + assertions = Assert.prototype; + + function applyCurrent( current ) { + return function() { + var assert = new Assert( QUnit.config.current ); + current.apply( assert, arguments ); + }; + } + + for ( i in assertions ) { + QUnit[ i ] = applyCurrent( assertions[ i ] ); + } + })(); + + (function() { + var i, l, + keys = [ + "test", + "module", + "expect", + "asyncTest", + "start", + "stop", + "ok", + "notOk", + "equal", + "notEqual", + "propEqual", + "notPropEqual", + "deepEqual", + "notDeepEqual", + "strictEqual", + "notStrictEqual", + "throws" + ]; + + for ( i = 0, l = keys.length; i < l; i++ ) { + window[ keys[ i ] ] = QUnit[ keys[ i ] ]; + } + })(); + + window.QUnit = QUnit; } -//from jquery.js -function inArray( elem, array ) { - if ( array.indexOf ) { - return array.indexOf( elem ); - } +// For nodejs +if ( typeof module !== "undefined" && module && module.exports ) { + module.exports = QUnit; - for ( var i = 0, length = array.length; i < length; i++ ) { - if ( array[ i ] === elem ) { - return i; - } - } + // For consistency with CommonJS environments' exports + module.exports.QUnit = QUnit; +} - return -1; +// For CommonJS with exports, but without module.exports, like Rhino +if ( typeof exports !== "undefined" && exports ) { + exports.QUnit = QUnit; +} + +if ( typeof define === "function" && define.amd ) { + define( function() { + return QUnit; + } ); + QUnit.config.autostart = false; } +// Get a reference to the global object, like window in browsers +}( (function() { + return this; +})() )); + +/*istanbul ignore next */ +// jscs:disable maximumLineLength /* - * Javascript Diff Algorithm - * By John Resig (http://ejohn.org/) - * Modified by Chu Alan "sprite" + * This file is a modified version of google-diff-match-patch's JavaScript implementation + * (https://code.google.com/p/google-diff-match-patch/source/browse/trunk/javascript/diff_match_patch_uncompressed.js), + * modifications are licensed as more fully set forth in LICENSE.txt. + * + * The original source of google-diff-match-patch is attributable and licensed as follows: + * + * Copyright 2006 Google Inc. + * http://code.google.com/p/google-diff-match-patch/ + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * Released under the MIT license. + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * * More Info: - * http://ejohn.org/projects/javascript-diff-algorithm/ + * https://code.google.com/p/google-diff-match-patch/ * * Usage: QUnit.diff(expected, actual) * - * QUnit.diff("the quick brown fox jumped over", "the quick fox jumps over") == "the quick <del>brown </del> fox <del>jumped </del><ins>jumps </ins> over" + * QUnit.diff( "the quick brown fox jumped over", "the quick fox jumps over" ) === "the quick <del>brown </del> fox jump<ins>s</ins><del>ed</del over" */ QUnit.diff = (function() { - function diff(o, n) { - var ns = {}; - var os = {}; - var i; - - for (i = 0; i < n.length; i++) { - if (ns[n[i]] == null) { - ns[n[i]] = { - rows: [], - o: null - }; + + function DiffMatchPatch() { + + // Defaults. + // Redefine these in your program to override the defaults. + + // Number of seconds to map a diff before giving up (0 for infinity). + this.DiffTimeout = 1.0; + // Cost of an empty edit operation in terms of edit characters. + this.DiffEditCost = 4; + } + + // DIFF FUNCTIONS + + /** + * The data structure representing a diff is an array of tuples: + * [[DIFF_DELETE, 'Hello'], [DIFF_INSERT, 'Goodbye'], [DIFF_EQUAL, ' world.']] + * which means: delete 'Hello', add 'Goodbye' and keep ' world.' + */ + var DIFF_DELETE = -1, + DIFF_INSERT = 1, + DIFF_EQUAL = 0; + + /** + * Find the differences between two texts. Simplifies the problem by stripping + * any common prefix or suffix off the texts before diffing. + * @param {string} text1 Old string to be diffed. + * @param {string} text2 New string to be diffed. + * @param {boolean=} optChecklines Optional speedup flag. If present and false, + * then don't run a line-level diff first to identify the changed areas. + * Defaults to true, which does a faster, slightly less optimal diff. + * @param {number} optDeadline Optional time when the diff should be complete + * by. Used internally for recursive calls. Users should set DiffTimeout + * instead. + * @return {!Array.<!DiffMatchPatch.Diff>} Array of diff tuples. + */ + DiffMatchPatch.prototype.DiffMain = function( text1, text2, optChecklines, optDeadline ) { + var deadline, checklines, commonlength, + commonprefix, commonsuffix, diffs; + // Set a deadline by which time the diff must be complete. + if ( typeof optDeadline === "undefined" ) { + if ( this.DiffTimeout <= 0 ) { + optDeadline = Number.MAX_VALUE; + } else { + optDeadline = ( new Date() ).getTime() + this.DiffTimeout * 1000; + } + } + deadline = optDeadline; + + // Check for null inputs. + if ( text1 === null || text2 === null ) { + throw new Error( "Null input. (DiffMain)" ); + } + + // Check for equality (speedup). + if ( text1 === text2 ) { + if ( text1 ) { + return [ + [ DIFF_EQUAL, text1 ] + ]; + } + return []; + } + + if ( typeof optChecklines === "undefined" ) { + optChecklines = true; + } + + checklines = optChecklines; + + // Trim off common prefix (speedup). + commonlength = this.diffCommonPrefix( text1, text2 ); + commonprefix = text1.substring( 0, commonlength ); + text1 = text1.substring( commonlength ); + text2 = text2.substring( commonlength ); + + // Trim off common suffix (speedup). + ///////// + commonlength = this.diffCommonSuffix( text1, text2 ); + commonsuffix = text1.substring( text1.length - commonlength ); + text1 = text1.substring( 0, text1.length - commonlength ); + text2 = text2.substring( 0, text2.length - commonlength ); + + // Compute the diff on the middle block. + diffs = this.diffCompute( text1, text2, checklines, deadline ); + + // Restore the prefix and suffix. + if ( commonprefix ) { + diffs.unshift( [ DIFF_EQUAL, commonprefix ] ); + } + if ( commonsuffix ) { + diffs.push( [ DIFF_EQUAL, commonsuffix ] ); + } + this.diffCleanupMerge( diffs ); + return diffs; + }; + + /** + * Reduce the number of edits by eliminating operationally trivial equalities. + * @param {!Array.<!DiffMatchPatch.Diff>} diffs Array of diff tuples. + */ + DiffMatchPatch.prototype.diffCleanupEfficiency = function( diffs ) { + var changes, equalities, equalitiesLength, lastequality, + pointer, preIns, preDel, postIns, postDel; + changes = false; + equalities = []; // Stack of indices where equalities are found. + equalitiesLength = 0; // Keeping our own length var is faster in JS. + /** @type {?string} */ + lastequality = null; + // Always equal to diffs[equalities[equalitiesLength - 1]][1] + pointer = 0; // Index of current position. + // Is there an insertion operation before the last equality. + preIns = false; + // Is there a deletion operation before the last equality. + preDel = false; + // Is there an insertion operation after the last equality. + postIns = false; + // Is there a deletion operation after the last equality. + postDel = false; + while ( pointer < diffs.length ) { + if ( diffs[ pointer ][ 0 ] === DIFF_EQUAL ) { // Equality found. + if ( diffs[ pointer ][ 1 ].length < this.DiffEditCost && ( postIns || postDel ) ) { + // Candidate found. + equalities[ equalitiesLength++ ] = pointer; + preIns = postIns; + preDel = postDel; + lastequality = diffs[ pointer ][ 1 ]; + } else { + // Not a candidate, and can never become one. + equalitiesLength = 0; + lastequality = null; + } + postIns = postDel = false; + } else { // An insertion or deletion. + if ( diffs[ pointer ][ 0 ] === DIFF_DELETE ) { + postDel = true; + } else { + postIns = true; + } + /* + * Five types to be split: + * <ins>A</ins><del>B</del>XY<ins>C</ins><del>D</del> + * <ins>A</ins>X<ins>C</ins><del>D</del> + * <ins>A</ins><del>B</del>X<ins>C</ins> + * <ins>A</del>X<ins>C</ins><del>D</del> + * <ins>A</ins><del>B</del>X<del>C</del> + */ + if ( lastequality && ( ( preIns && preDel && postIns && postDel ) || + ( ( lastequality.length < this.DiffEditCost / 2 ) && + ( preIns + preDel + postIns + postDel ) === 3 ) ) ) { + // Duplicate record. + diffs.splice( equalities[equalitiesLength - 1], 0, [ DIFF_DELETE, lastequality ] ); + // Change second copy to insert. + diffs[ equalities[ equalitiesLength - 1 ] + 1 ][ 0 ] = DIFF_INSERT; + equalitiesLength--; // Throw away the equality we just deleted; + lastequality = null; + if (preIns && preDel) { + // No changes made which could affect previous entry, keep going. + postIns = postDel = true; + equalitiesLength = 0; + } else { + equalitiesLength--; // Throw away the previous equality. + pointer = equalitiesLength > 0 ? equalities[ equalitiesLength - 1 ] : -1; + postIns = postDel = false; + } + changes = true; + } + } + pointer++; + } + + if ( changes ) { + this.diffCleanupMerge( diffs ); + } + }; + + /** + * Convert a diff array into a pretty HTML report. + * @param {!Array.<!DiffMatchPatch.Diff>} diffs Array of diff tuples. + * @param {integer} string to be beautified. + * @return {string} HTML representation. + */ + DiffMatchPatch.prototype.diffPrettyHtml = function( diffs ) { + var op, data, x, html = []; + for ( x = 0; x < diffs.length; x++ ) { + op = diffs[x][0]; // Operation (insert, delete, equal) + data = diffs[x][1]; // Text of change. + switch ( op ) { + case DIFF_INSERT: + html[x] = "<ins>" + data + "</ins>"; + break; + case DIFF_DELETE: + html[x] = "<del>" + data + "</del>"; + break; + case DIFF_EQUAL: + html[x] = "<span>" + data + "</span>"; + break; + } + } + return html.join(""); + }; + + /** + * Determine the common prefix of two strings. + * @param {string} text1 First string. + * @param {string} text2 Second string. + * @return {number} The number of characters common to the start of each + * string. + */ + DiffMatchPatch.prototype.diffCommonPrefix = function( text1, text2 ) { + var pointermid, pointermax, pointermin, pointerstart; + // Quick check for common null cases. + if ( !text1 || !text2 || text1.charAt(0) !== text2.charAt(0) ) { + return 0; + } + // Binary search. + // Performance analysis: http://neil.fraser.name/news/2007/10/09/ + pointermin = 0; + pointermax = Math.min( text1.length, text2.length ); + pointermid = pointermax; + pointerstart = 0; + while ( pointermin < pointermid ) { + if ( text1.substring( pointerstart, pointermid ) === text2.substring( pointerstart, pointermid ) ) { + pointermin = pointermid; + pointerstart = pointermin; + } else { + pointermax = pointermid; + } + pointermid = Math.floor( ( pointermax - pointermin ) / 2 + pointermin ); + } + return pointermid; + }; + + /** + * Determine the common suffix of two strings. + * @param {string} text1 First string. + * @param {string} text2 Second string. + * @return {number} The number of characters common to the end of each string. + */ + DiffMatchPatch.prototype.diffCommonSuffix = function( text1, text2 ) { + var pointermid, pointermax, pointermin, pointerend; + // Quick check for common null cases. + if (!text1 || !text2 || text1.charAt(text1.length - 1) !== text2.charAt(text2.length - 1)) { + return 0; + } + // Binary search. + // Performance analysis: http://neil.fraser.name/news/2007/10/09/ + pointermin = 0; + pointermax = Math.min(text1.length, text2.length); + pointermid = pointermax; + pointerend = 0; + while ( pointermin < pointermid ) { + if (text1.substring( text1.length - pointermid, text1.length - pointerend ) === + text2.substring( text2.length - pointermid, text2.length - pointerend ) ) { + pointermin = pointermid; + pointerend = pointermin; + } else { + pointermax = pointermid; + } + pointermid = Math.floor( ( pointermax - pointermin ) / 2 + pointermin ); + } + return pointermid; + }; + + /** + * Find the differences between two texts. Assumes that the texts do not + * have any common prefix or suffix. + * @param {string} text1 Old string to be diffed. + * @param {string} text2 New string to be diffed. + * @param {boolean} checklines Speedup flag. If false, then don't run a + * line-level diff first to identify the changed areas. + * If true, then run a faster, slightly less optimal diff. + * @param {number} deadline Time when the diff should be complete by. + * @return {!Array.<!DiffMatchPatch.Diff>} Array of diff tuples. + * @private + */ + DiffMatchPatch.prototype.diffCompute = function( text1, text2, checklines, deadline ) { + var diffs, longtext, shorttext, i, hm, + text1A, text2A, text1B, text2B, + midCommon, diffsA, diffsB; + + if ( !text1 ) { + // Just add some text (speedup). + return [ + [ DIFF_INSERT, text2 ] + ]; + } + + if (!text2) { + // Just delete some text (speedup). + return [ + [ DIFF_DELETE, text1 ] + ]; + } + + longtext = text1.length > text2.length ? text1 : text2; + shorttext = text1.length > text2.length ? text2 : text1; + i = longtext.indexOf( shorttext ); + if ( i !== -1 ) { + // Shorter text is inside the longer text (speedup). + diffs = [ + [ DIFF_INSERT, longtext.substring( 0, i ) ], + [ DIFF_EQUAL, shorttext ], + [ DIFF_INSERT, longtext.substring( i + shorttext.length ) ] + ]; + // Swap insertions for deletions if diff is reversed. + if ( text1.length > text2.length ) { + diffs[0][0] = diffs[2][0] = DIFF_DELETE; + } + return diffs; + } + + if ( shorttext.length === 1 ) { + // Single character string. + // After the previous speedup, the character can't be an equality. + return [ + [ DIFF_DELETE, text1 ], + [ DIFF_INSERT, text2 ] + ]; + } + + // Check to see if the problem can be split in two. + hm = this.diffHalfMatch(text1, text2); + if (hm) { + // A half-match was found, sort out the return data. + text1A = hm[0]; + text1B = hm[1]; + text2A = hm[2]; + text2B = hm[3]; + midCommon = hm[4]; + // Send both pairs off for separate processing. + diffsA = this.DiffMain(text1A, text2A, checklines, deadline); + diffsB = this.DiffMain(text1B, text2B, checklines, deadline); + // Merge the results. + return diffsA.concat([ + [ DIFF_EQUAL, midCommon ] + ], diffsB); + } + + if (checklines && text1.length > 100 && text2.length > 100) { + return this.diffLineMode(text1, text2, deadline); + } + + return this.diffBisect(text1, text2, deadline); + }; + + /** + * Do the two texts share a substring which is at least half the length of the + * longer text? + * This speedup can produce non-minimal diffs. + * @param {string} text1 First string. + * @param {string} text2 Second string. + * @return {Array.<string>} Five element Array, containing the prefix of + * text1, the suffix of text1, the prefix of text2, the suffix of + * text2 and the common middle. Or null if there was no match. + * @private + */ + DiffMatchPatch.prototype.diffHalfMatch = function(text1, text2) { + var longtext, shorttext, dmp, + text1A, text2B, text2A, text1B, midCommon, + hm1, hm2, hm; + if (this.DiffTimeout <= 0) { + // Don't risk returning a non-optimal diff if we have unlimited time. + return null; + } + longtext = text1.length > text2.length ? text1 : text2; + shorttext = text1.length > text2.length ? text2 : text1; + if (longtext.length < 4 || shorttext.length * 2 < longtext.length) { + return null; // Pointless. + } + dmp = this; // 'this' becomes 'window' in a closure. + + /** + * Does a substring of shorttext exist within longtext such that the substring + * is at least half the length of longtext? + * Closure, but does not reference any external variables. + * @param {string} longtext Longer string. + * @param {string} shorttext Shorter string. + * @param {number} i Start index of quarter length substring within longtext. + * @return {Array.<string>} Five element Array, containing the prefix of + * longtext, the suffix of longtext, the prefix of shorttext, the suffix + * of shorttext and the common middle. Or null if there was no match. + * @private + */ + function diffHalfMatchI(longtext, shorttext, i) { + var seed, j, bestCommon, prefixLength, suffixLength, + bestLongtextA, bestLongtextB, bestShorttextA, bestShorttextB; + // Start with a 1/4 length substring at position i as a seed. + seed = longtext.substring(i, i + Math.floor(longtext.length / 4)); + j = -1; + bestCommon = ""; + while ((j = shorttext.indexOf(seed, j + 1)) !== -1) { + prefixLength = dmp.diffCommonPrefix(longtext.substring(i), + shorttext.substring(j)); + suffixLength = dmp.diffCommonSuffix(longtext.substring(0, i), + shorttext.substring(0, j)); + if (bestCommon.length < suffixLength + prefixLength) { + bestCommon = shorttext.substring(j - suffixLength, j) + + shorttext.substring(j, j + prefixLength); + bestLongtextA = longtext.substring(0, i - suffixLength); + bestLongtextB = longtext.substring(i + prefixLength); + bestShorttextA = shorttext.substring(0, j - suffixLength); + bestShorttextB = shorttext.substring(j + prefixLength); + } + } + if (bestCommon.length * 2 >= longtext.length) { + return [ bestLongtextA, bestLongtextB, + bestShorttextA, bestShorttextB, bestCommon + ]; + } else { + return null; + } + } + + // First check if the second quarter is the seed for a half-match. + hm1 = diffHalfMatchI(longtext, shorttext, + Math.ceil(longtext.length / 4)); + // Check again based on the third quarter. + hm2 = diffHalfMatchI(longtext, shorttext, + Math.ceil(longtext.length / 2)); + if (!hm1 && !hm2) { + return null; + } else if (!hm2) { + hm = hm1; + } else if (!hm1) { + hm = hm2; + } else { + // Both matched. Select the longest. + hm = hm1[4].length > hm2[4].length ? hm1 : hm2; + } + + // A half-match was found, sort out the return data. + text1A, text1B, text2A, text2B; + if (text1.length > text2.length) { + text1A = hm[0]; + text1B = hm[1]; + text2A = hm[2]; + text2B = hm[3]; + } else { + text2A = hm[0]; + text2B = hm[1]; + text1A = hm[2]; + text1B = hm[3]; + } + midCommon = hm[4]; + return [ text1A, text1B, text2A, text2B, midCommon ]; + }; + + /** + * Do a quick line-level diff on both strings, then rediff the parts for + * greater accuracy. + * This speedup can produce non-minimal diffs. + * @param {string} text1 Old string to be diffed. + * @param {string} text2 New string to be diffed. + * @param {number} deadline Time when the diff should be complete by. + * @return {!Array.<!DiffMatchPatch.Diff>} Array of diff tuples. + * @private + */ + DiffMatchPatch.prototype.diffLineMode = function(text1, text2, deadline) { + var a, diffs, linearray, pointer, countInsert, + countDelete, textInsert, textDelete, j; + // Scan the text on a line-by-line basis first. + a = this.diffLinesToChars(text1, text2); + text1 = a.chars1; + text2 = a.chars2; + linearray = a.lineArray; + + diffs = this.DiffMain(text1, text2, false, deadline); + + // Convert the diff back to original text. + this.diffCharsToLines(diffs, linearray); + // Eliminate freak matches (e.g. blank lines) + this.diffCleanupSemantic(diffs); + + // Rediff any replacement blocks, this time character-by-character. + // Add a dummy entry at the end. + diffs.push( [ DIFF_EQUAL, "" ] ); + pointer = 0; + countDelete = 0; + countInsert = 0; + textDelete = ""; + textInsert = ""; + while (pointer < diffs.length) { + switch ( diffs[pointer][0] ) { + case DIFF_INSERT: + countInsert++; + textInsert += diffs[pointer][1]; + break; + case DIFF_DELETE: + countDelete++; + textDelete += diffs[pointer][1]; + break; + case DIFF_EQUAL: + // Upon reaching an equality, check for prior redundancies. + if (countDelete >= 1 && countInsert >= 1) { + // Delete the offending records and add the merged ones. + diffs.splice(pointer - countDelete - countInsert, + countDelete + countInsert); + pointer = pointer - countDelete - countInsert; + a = this.DiffMain(textDelete, textInsert, false, deadline); + for (j = a.length - 1; j >= 0; j--) { + diffs.splice( pointer, 0, a[j] ); + } + pointer = pointer + a.length; + } + countInsert = 0; + countDelete = 0; + textDelete = ""; + textInsert = ""; + break; + } + pointer++; + } + diffs.pop(); // Remove the dummy entry at the end. + + return diffs; + }; + + /** + * Find the 'middle snake' of a diff, split the problem in two + * and return the recursively constructed diff. + * See Myers 1986 paper: An O(ND) Difference Algorithm and Its Variations. + * @param {string} text1 Old string to be diffed. + * @param {string} text2 New string to be diffed. + * @param {number} deadline Time at which to bail if not yet complete. + * @return {!Array.<!DiffMatchPatch.Diff>} Array of diff tuples. + * @private + */ + DiffMatchPatch.prototype.diffBisect = function(text1, text2, deadline) { + var text1Length, text2Length, maxD, vOffset, vLength, + v1, v2, x, delta, front, k1start, k1end, k2start, + k2end, k2Offset, k1Offset, x1, x2, y1, y2, d, k1, k2; + // Cache the text lengths to prevent multiple calls. + text1Length = text1.length; + text2Length = text2.length; + maxD = Math.ceil((text1Length + text2Length) / 2); + vOffset = maxD; + vLength = 2 * maxD; + v1 = new Array(vLength); + v2 = new Array(vLength); + // Setting all elements to -1 is faster in Chrome & Firefox than mixing + // integers and undefined. + for (x = 0; x < vLength; x++) { + v1[x] = -1; + v2[x] = -1; + } + v1[vOffset + 1] = 0; + v2[vOffset + 1] = 0; + delta = text1Length - text2Length; + // If the total number of characters is odd, then the front path will collide + // with the reverse path. + front = (delta % 2 !== 0); + // Offsets for start and end of k loop. + // Prevents mapping of space beyond the grid. + k1start = 0; + k1end = 0; + k2start = 0; + k2end = 0; + for (d = 0; d < maxD; d++) { + // Bail out if deadline is reached. + if ((new Date()).getTime() > deadline) { + break; + } + + // Walk the front path one step. + for (k1 = -d + k1start; k1 <= d - k1end; k1 += 2) { + k1Offset = vOffset + k1; + if ( k1 === -d || ( k1 !== d && v1[ k1Offset - 1 ] < v1[ k1Offset + 1 ] ) ) { + x1 = v1[k1Offset + 1]; + } else { + x1 = v1[k1Offset - 1] + 1; + } + y1 = x1 - k1; + while (x1 < text1Length && y1 < text2Length && + text1.charAt(x1) === text2.charAt(y1)) { + x1++; + y1++; + } + v1[k1Offset] = x1; + if (x1 > text1Length) { + // Ran off the right of the graph. + k1end += 2; + } else if (y1 > text2Length) { + // Ran off the bottom of the graph. + k1start += 2; + } else if (front) { + k2Offset = vOffset + delta - k1; + if (k2Offset >= 0 && k2Offset < vLength && v2[k2Offset] !== -1) { + // Mirror x2 onto top-left coordinate system. + x2 = text1Length - v2[k2Offset]; + if (x1 >= x2) { + // Overlap detected. + return this.diffBisectSplit(text1, text2, x1, y1, deadline); + } + } + } + } + + // Walk the reverse path one step. + for (k2 = -d + k2start; k2 <= d - k2end; k2 += 2) { + k2Offset = vOffset + k2; + if ( k2 === -d || (k2 !== d && v2[ k2Offset - 1 ] < v2[ k2Offset + 1 ] ) ) { + x2 = v2[k2Offset + 1]; + } else { + x2 = v2[k2Offset - 1] + 1; + } + y2 = x2 - k2; + while (x2 < text1Length && y2 < text2Length && + text1.charAt(text1Length - x2 - 1) === + text2.charAt(text2Length - y2 - 1)) { + x2++; + y2++; + } + v2[k2Offset] = x2; + if (x2 > text1Length) { + // Ran off the left of the graph. + k2end += 2; + } else if (y2 > text2Length) { + // Ran off the top of the graph. + k2start += 2; + } else if (!front) { + k1Offset = vOffset + delta - k2; + if (k1Offset >= 0 && k1Offset < vLength && v1[k1Offset] !== -1) { + x1 = v1[k1Offset]; + y1 = vOffset + x1 - k1Offset; + // Mirror x2 onto top-left coordinate system. + x2 = text1Length - x2; + if (x1 >= x2) { + // Overlap detected. + return this.diffBisectSplit(text1, text2, x1, y1, deadline); + } + } + } + } + } + // Diff took too long and hit the deadline or + // number of diffs equals number of characters, no commonality at all. + return [ + [ DIFF_DELETE, text1 ], + [ DIFF_INSERT, text2 ] + ]; + }; + + /** + * Given the location of the 'middle snake', split the diff in two parts + * and recurse. + * @param {string} text1 Old string to be diffed. + * @param {string} text2 New string to be diffed. + * @param {number} x Index of split point in text1. + * @param {number} y Index of split point in text2. + * @param {number} deadline Time at which to bail if not yet complete. + * @return {!Array.<!DiffMatchPatch.Diff>} Array of diff tuples. + * @private + */ + DiffMatchPatch.prototype.diffBisectSplit = function( text1, text2, x, y, deadline ) { + var text1a, text1b, text2a, text2b, diffs, diffsb; + text1a = text1.substring(0, x); + text2a = text2.substring(0, y); + text1b = text1.substring(x); + text2b = text2.substring(y); + + // Compute both diffs serially. + diffs = this.DiffMain(text1a, text2a, false, deadline); + diffsb = this.DiffMain(text1b, text2b, false, deadline); + + return diffs.concat(diffsb); + }; + + /** + * Reduce the number of edits by eliminating semantically trivial equalities. + * @param {!Array.<!DiffMatchPatch.Diff>} diffs Array of diff tuples. + */ + DiffMatchPatch.prototype.diffCleanupSemantic = function(diffs) { + var changes, equalities, equalitiesLength, lastequality, + pointer, lengthInsertions2, lengthDeletions2, lengthInsertions1, + lengthDeletions1, deletion, insertion, overlapLength1, overlapLength2; + changes = false; + equalities = []; // Stack of indices where equalities are found. + equalitiesLength = 0; // Keeping our own length var is faster in JS. + /** @type {?string} */ + lastequality = null; + // Always equal to diffs[equalities[equalitiesLength - 1]][1] + pointer = 0; // Index of current position. + // Number of characters that changed prior to the equality. + lengthInsertions1 = 0; + lengthDeletions1 = 0; + // Number of characters that changed after the equality. + lengthInsertions2 = 0; + lengthDeletions2 = 0; + while (pointer < diffs.length) { + if (diffs[pointer][0] === DIFF_EQUAL) { // Equality found. + equalities[equalitiesLength++] = pointer; + lengthInsertions1 = lengthInsertions2; + lengthDeletions1 = lengthDeletions2; + lengthInsertions2 = 0; + lengthDeletions2 = 0; + lastequality = diffs[pointer][1]; + } else { // An insertion or deletion. + if (diffs[pointer][0] === DIFF_INSERT) { + lengthInsertions2 += diffs[pointer][1].length; + } else { + lengthDeletions2 += diffs[pointer][1].length; + } + // Eliminate an equality that is smaller or equal to the edits on both + // sides of it. + if (lastequality && (lastequality.length <= + Math.max(lengthInsertions1, lengthDeletions1)) && + (lastequality.length <= Math.max(lengthInsertions2, + lengthDeletions2))) { + // Duplicate record. + diffs.splice( equalities[ equalitiesLength - 1 ], 0, [ DIFF_DELETE, lastequality ] ); + // Change second copy to insert. + diffs[equalities[equalitiesLength - 1] + 1][0] = DIFF_INSERT; + // Throw away the equality we just deleted. + equalitiesLength--; + // Throw away the previous equality (it needs to be reevaluated). + equalitiesLength--; + pointer = equalitiesLength > 0 ? equalities[equalitiesLength - 1] : -1; + lengthInsertions1 = 0; // Reset the counters. + lengthDeletions1 = 0; + lengthInsertions2 = 0; + lengthDeletions2 = 0; + lastequality = null; + changes = true; + } + } + pointer++; + } + + // Normalize the diff. + if (changes) { + this.diffCleanupMerge(diffs); + } + + // Find any overlaps between deletions and insertions. + // e.g: <del>abcxxx</del><ins>xxxdef</ins> + // -> <del>abc</del>xxx<ins>def</ins> + // e.g: <del>xxxabc</del><ins>defxxx</ins> + // -> <ins>def</ins>xxx<del>abc</del> + // Only extract an overlap if it is as big as the edit ahead or behind it. + pointer = 1; + while (pointer < diffs.length) { + if (diffs[pointer - 1][0] === DIFF_DELETE && + diffs[pointer][0] === DIFF_INSERT) { + deletion = diffs[pointer - 1][1]; + insertion = diffs[pointer][1]; + overlapLength1 = this.diffCommonOverlap(deletion, insertion); + overlapLength2 = this.diffCommonOverlap(insertion, deletion); + if (overlapLength1 >= overlapLength2) { + if (overlapLength1 >= deletion.length / 2 || + overlapLength1 >= insertion.length / 2) { + // Overlap found. Insert an equality and trim the surrounding edits. + diffs.splice( pointer, 0, [ DIFF_EQUAL, insertion.substring( 0, overlapLength1 ) ] ); + diffs[pointer - 1][1] = + deletion.substring(0, deletion.length - overlapLength1); + diffs[pointer + 1][1] = insertion.substring(overlapLength1); + pointer++; + } + } else { + if (overlapLength2 >= deletion.length / 2 || + overlapLength2 >= insertion.length / 2) { + // Reverse overlap found. + // Insert an equality and swap and trim the surrounding edits. + diffs.splice( pointer, 0, [ DIFF_EQUAL, deletion.substring( 0, overlapLength2 ) ] ); + diffs[pointer - 1][0] = DIFF_INSERT; + diffs[pointer - 1][1] = + insertion.substring(0, insertion.length - overlapLength2); + diffs[pointer + 1][0] = DIFF_DELETE; + diffs[pointer + 1][1] = + deletion.substring(overlapLength2); + pointer++; + } + } + pointer++; + } + pointer++; + } + }; + + /** + * Determine if the suffix of one string is the prefix of another. + * @param {string} text1 First string. + * @param {string} text2 Second string. + * @return {number} The number of characters common to the end of the first + * string and the start of the second string. + * @private + */ + DiffMatchPatch.prototype.diffCommonOverlap = function(text1, text2) { + var text1Length, text2Length, textLength, + best, length, pattern, found; + // Cache the text lengths to prevent multiple calls. + text1Length = text1.length; + text2Length = text2.length; + // Eliminate the null case. + if (text1Length === 0 || text2Length === 0) { + return 0; + } + // Truncate the longer string. + if (text1Length > text2Length) { + text1 = text1.substring(text1Length - text2Length); + } else if (text1Length < text2Length) { + text2 = text2.substring(0, text1Length); + } + textLength = Math.min(text1Length, text2Length); + // Quick check for the worst case. + if (text1 === text2) { + return textLength; + } + + // Start by looking for a single character match + // and increase length until no match is found. + // Performance analysis: http://neil.fraser.name/news/2010/11/04/ + best = 0; + length = 1; + while (true) { + pattern = text1.substring(textLength - length); + found = text2.indexOf(pattern); + if (found === -1) { + return best; + } + length += found; + if (found === 0 || text1.substring(textLength - length) === + text2.substring(0, length)) { + best = length; + length++; + } + } + }; + + /** + * Split two texts into an array of strings. Reduce the texts to a string of + * hashes where each Unicode character represents one line. + * @param {string} text1 First string. + * @param {string} text2 Second string. + * @return {{chars1: string, chars2: string, lineArray: !Array.<string>}} + * An object containing the encoded text1, the encoded text2 and + * the array of unique strings. + * The zeroth element of the array of unique strings is intentionally blank. + * @private + */ + DiffMatchPatch.prototype.diffLinesToChars = function(text1, text2) { + var lineArray, lineHash, chars1, chars2; + lineArray = []; // e.g. lineArray[4] === 'Hello\n' + lineHash = {}; // e.g. lineHash['Hello\n'] === 4 + + // '\x00' is a valid character, but various debuggers don't like it. + // So we'll insert a junk entry to avoid generating a null character. + lineArray[0] = ""; + + /** + * Split a text into an array of strings. Reduce the texts to a string of + * hashes where each Unicode character represents one line. + * Modifies linearray and linehash through being a closure. + * @param {string} text String to encode. + * @return {string} Encoded string. + * @private + */ + function diffLinesToCharsMunge(text) { + var chars, lineStart, lineEnd, lineArrayLength, line; + chars = ""; + // Walk the text, pulling out a substring for each line. + // text.split('\n') would would temporarily double our memory footprint. + // Modifying text would create many large strings to garbage collect. + lineStart = 0; + lineEnd = -1; + // Keeping our own length variable is faster than looking it up. + lineArrayLength = lineArray.length; + while (lineEnd < text.length - 1) { + lineEnd = text.indexOf("\n", lineStart); + if (lineEnd === -1) { + lineEnd = text.length - 1; + } + line = text.substring(lineStart, lineEnd + 1); + lineStart = lineEnd + 1; + + if (lineHash.hasOwnProperty ? lineHash.hasOwnProperty(line) : + (lineHash[line] !== undefined)) { + chars += String.fromCharCode( lineHash[ line ] ); + } else { + chars += String.fromCharCode(lineArrayLength); + lineHash[line] = lineArrayLength; + lineArray[lineArrayLength++] = line; + } + } + return chars; + } + + chars1 = diffLinesToCharsMunge(text1); + chars2 = diffLinesToCharsMunge(text2); + return { + chars1: chars1, + chars2: chars2, + lineArray: lineArray + }; + }; + + /** + * Rehydrate the text in a diff from a string of line hashes to real lines of + * text. + * @param {!Array.<!DiffMatchPatch.Diff>} diffs Array of diff tuples. + * @param {!Array.<string>} lineArray Array of unique strings. + * @private + */ + DiffMatchPatch.prototype.diffCharsToLines = function( diffs, lineArray ) { + var x, chars, text, y; + for ( x = 0; x < diffs.length; x++ ) { + chars = diffs[x][1]; + text = []; + for ( y = 0; y < chars.length; y++ ) { + text[y] = lineArray[chars.charCodeAt(y)]; + } + diffs[x][1] = text.join(""); + } + }; + + /** + * Reorder and merge like edit sections. Merge equalities. + * Any edit section can move as long as it doesn't cross an equality. + * @param {!Array.<!DiffMatchPatch.Diff>} diffs Array of diff tuples. + */ + DiffMatchPatch.prototype.diffCleanupMerge = function(diffs) { + var pointer, countDelete, countInsert, textInsert, textDelete, + commonlength, changes; + diffs.push( [ DIFF_EQUAL, "" ] ); // Add a dummy entry at the end. + pointer = 0; + countDelete = 0; + countInsert = 0; + textDelete = ""; + textInsert = ""; + commonlength; + while (pointer < diffs.length) { + switch ( diffs[ pointer ][ 0 ] ) { + case DIFF_INSERT: + countInsert++; + textInsert += diffs[pointer][1]; + pointer++; + break; + case DIFF_DELETE: + countDelete++; + textDelete += diffs[pointer][1]; + pointer++; + break; + case DIFF_EQUAL: + // Upon reaching an equality, check for prior redundancies. + if (countDelete + countInsert > 1) { + if (countDelete !== 0 && countInsert !== 0) { + // Factor out any common prefixies. + commonlength = this.diffCommonPrefix(textInsert, textDelete); + if (commonlength !== 0) { + if ((pointer - countDelete - countInsert) > 0 && + diffs[pointer - countDelete - countInsert - 1][0] === + DIFF_EQUAL) { + diffs[pointer - countDelete - countInsert - 1][1] += + textInsert.substring(0, commonlength); + } else { + diffs.splice( 0, 0, [ DIFF_EQUAL, + textInsert.substring( 0, commonlength ) + ] ); + pointer++; + } + textInsert = textInsert.substring(commonlength); + textDelete = textDelete.substring(commonlength); + } + // Factor out any common suffixies. + commonlength = this.diffCommonSuffix(textInsert, textDelete); + if (commonlength !== 0) { + diffs[pointer][1] = textInsert.substring(textInsert.length - + commonlength) + diffs[pointer][1]; + textInsert = textInsert.substring(0, textInsert.length - + commonlength); + textDelete = textDelete.substring(0, textDelete.length - + commonlength); + } + } + // Delete the offending records and add the merged ones. + if (countDelete === 0) { + diffs.splice( pointer - countInsert, + countDelete + countInsert, [ DIFF_INSERT, textInsert ] ); + } else if (countInsert === 0) { + diffs.splice( pointer - countDelete, + countDelete + countInsert, [ DIFF_DELETE, textDelete ] ); + } else { + diffs.splice( pointer - countDelete - countInsert, + countDelete + countInsert, [ DIFF_DELETE, textDelete ], [ DIFF_INSERT, textInsert ] ); + } + pointer = pointer - countDelete - countInsert + + (countDelete ? 1 : 0) + (countInsert ? 1 : 0) + 1; + } else if (pointer !== 0 && diffs[pointer - 1][0] === DIFF_EQUAL) { + // Merge this equality with the previous one. + diffs[pointer - 1][1] += diffs[pointer][1]; + diffs.splice(pointer, 1); + } else { + pointer++; + } + countInsert = 0; + countDelete = 0; + textDelete = ""; + textInsert = ""; + break; + } + } + if (diffs[diffs.length - 1][1] === "") { + diffs.pop(); // Remove the dummy entry at the end. + } + + // Second pass: look for single edits surrounded on both sides by equalities + // which can be shifted sideways to eliminate an equality. + // e.g: A<ins>BA</ins>C -> <ins>AB</ins>AC + changes = false; + pointer = 1; + // Intentionally ignore the first and last element (don't need checking). + while (pointer < diffs.length - 1) { + if (diffs[pointer - 1][0] === DIFF_EQUAL && + diffs[pointer + 1][0] === DIFF_EQUAL) { + // This is a single edit surrounded by equalities. + if ( diffs[ pointer ][ 1 ].substring( diffs[ pointer ][ 1 ].length - + diffs[ pointer - 1 ][ 1 ].length ) === diffs[ pointer - 1 ][ 1 ] ) { + // Shift the edit over the previous equality. + diffs[pointer][1] = diffs[pointer - 1][1] + + diffs[pointer][1].substring(0, diffs[pointer][1].length - + diffs[pointer - 1][1].length); + diffs[pointer + 1][1] = diffs[pointer - 1][1] + diffs[pointer + 1][1]; + diffs.splice(pointer - 1, 1); + changes = true; + } else if ( diffs[ pointer ][ 1 ].substring( 0, diffs[ pointer + 1 ][ 1 ].length ) === + diffs[ pointer + 1 ][ 1 ] ) { + // Shift the edit over the next equality. + diffs[pointer - 1][1] += diffs[pointer + 1][1]; + diffs[pointer][1] = + diffs[pointer][1].substring(diffs[pointer + 1][1].length) + + diffs[pointer + 1][1]; + diffs.splice(pointer + 1, 1); + changes = true; + } + } + pointer++; + } + // If shifts were made, the diff needs reordering and another shift sweep. + if (changes) { + this.diffCleanupMerge(diffs); + } + }; + + return function(o, n) { + var diff, output, text; + diff = new DiffMatchPatch(); + output = diff.DiffMain(o, n); + //console.log(output); + diff.diffCleanupEfficiency(output); + text = diff.diffPrettyHtml(output); + + return text; + }; +}()); +// jscs:enable + +(function() { + +// Deprecated QUnit.init - Ref #530 +// Re-initialize the configuration options +QUnit.init = function() { + var tests, banner, result, qunit, + config = QUnit.config; + + config.stats = { all: 0, bad: 0 }; + config.moduleStats = { all: 0, bad: 0 }; + config.started = 0; + config.updateRate = 1000; + config.blocking = false; + config.autostart = true; + config.autorun = false; + config.filter = ""; + config.queue = []; + + // Return on non-browser environments + // This is necessary to not break on node tests + if ( typeof window === "undefined" ) { + return; + } + + qunit = id( "qunit" ); + if ( qunit ) { + qunit.innerHTML = + "<h1 id='qunit-header'>" + escapeText( document.title ) + "</h1>" + + "<h2 id='qunit-banner'></h2>" + + "<div id='qunit-testrunner-toolbar'></div>" + + "<h2 id='qunit-userAgent'></h2>" + + "<ol id='qunit-tests'></ol>"; + } + + tests = id( "qunit-tests" ); + banner = id( "qunit-banner" ); + result = id( "qunit-testresult" ); + + if ( tests ) { + tests.innerHTML = ""; + } + + if ( banner ) { + banner.className = ""; + } + + if ( result ) { + result.parentNode.removeChild( result ); + } + + if ( tests ) { + result = document.createElement( "p" ); + result.id = "qunit-testresult"; + result.className = "result"; + tests.parentNode.insertBefore( result, tests ); + result.innerHTML = "Running...<br /> "; + } +}; + +// Don't load the HTML Reporter on non-Browser environments +if ( typeof window === "undefined" ) { + return; +} + +var config = QUnit.config, + hasOwn = Object.prototype.hasOwnProperty, + defined = { + document: window.document !== undefined, + sessionStorage: (function() { + var x = "qunit-test-string"; + try { + sessionStorage.setItem( x, x ); + sessionStorage.removeItem( x ); + return true; + } catch ( e ) { + return false; } - ns[n[i]].rows.push(i); + }()) + }, + modulesList = []; + +/** +* Escape text for attribute or text content. +*/ +function escapeText( s ) { + if ( !s ) { + return ""; + } + s = s + ""; + + // Both single quotes and double quotes (for attributes) + return s.replace( /['"<>&]/g, function( s ) { + switch ( s ) { + case "'": + return "'"; + case "\"": + return """; + case "<": + return "<"; + case ">": + return ">"; + case "&": + return "&"; } + }); +} - for (i = 0; i < o.length; i++) { - if (os[o[i]] == null) { - os[o[i]] = { - rows: [], - n: null - }; +/** + * @param {HTMLElement} elem + * @param {string} type + * @param {Function} fn + */ +function addEvent( elem, type, fn ) { + if ( elem.addEventListener ) { + + // Standards-based browsers + elem.addEventListener( type, fn, false ); + } else if ( elem.attachEvent ) { + + // support: IE <9 + elem.attachEvent( "on" + type, function() { + var event = window.event; + if ( !event.target ) { + event.target = event.srcElement || document; } - os[o[i]].rows.push(i); + + fn.call( elem, event ); + }); + } +} + +/** + * @param {Array|NodeList} elems + * @param {string} type + * @param {Function} fn + */ +function addEvents( elems, type, fn ) { + var i = elems.length; + while ( i-- ) { + addEvent( elems[ i ], type, fn ); + } +} + +function hasClass( elem, name ) { + return ( " " + elem.className + " " ).indexOf( " " + name + " " ) >= 0; +} + +function addClass( elem, name ) { + if ( !hasClass( elem, name ) ) { + elem.className += ( elem.className ? " " : "" ) + name; + } +} + +function toggleClass( elem, name ) { + if ( hasClass( elem, name ) ) { + removeClass( elem, name ); + } else { + addClass( elem, name ); + } +} + +function removeClass( elem, name ) { + var set = " " + elem.className + " "; + + // Class name may appear multiple times + while ( set.indexOf( " " + name + " " ) >= 0 ) { + set = set.replace( " " + name + " ", " " ); + } + + // trim for prettiness + elem.className = typeof set.trim === "function" ? set.trim() : set.replace( /^\s+|\s+$/g, "" ); +} + +function id( name ) { + return defined.document && document.getElementById && document.getElementById( name ); +} + +function getUrlConfigHtml() { + var i, j, val, + escaped, escapedTooltip, + selection = false, + len = config.urlConfig.length, + urlConfigHtml = ""; + + for ( i = 0; i < len; i++ ) { + val = config.urlConfig[ i ]; + if ( typeof val === "string" ) { + val = { + id: val, + label: val + }; } - for (i in ns) { - if ( !hasOwn.call( ns, i ) ) { - continue; - } - if (ns[i].rows.length == 1 && typeof(os[i]) != "undefined" && os[i].rows.length == 1) { - n[ns[i].rows[0]] = { - text: n[ns[i].rows[0]], - row: os[i].rows[0] - }; - o[os[i].rows[0]] = { - text: o[os[i].rows[0]], - row: ns[i].rows[0] - }; - } + escaped = escapeText( val.id ); + escapedTooltip = escapeText( val.tooltip ); + + if ( config[ val.id ] === undefined ) { + config[ val.id ] = QUnit.urlParams[ val.id ]; } - for (i = 0; i < n.length - 1; i++) { - if (n[i].text != null && n[i + 1].text == null && n[i].row + 1 < o.length && o[n[i].row + 1].text == null && - n[i + 1] == o[n[i].row + 1]) { - n[i + 1] = { - text: n[i + 1], - row: n[i].row + 1 - }; - o[n[i].row + 1] = { - text: o[n[i].row + 1], - row: i + 1 - }; + if ( !val.value || typeof val.value === "string" ) { + urlConfigHtml += "<input id='qunit-urlconfig-" + escaped + + "' name='" + escaped + "' type='checkbox'" + + ( val.value ? " value='" + escapeText( val.value ) + "'" : "" ) + + ( config[ val.id ] ? " checked='checked'" : "" ) + + " title='" + escapedTooltip + "' /><label for='qunit-urlconfig-" + escaped + + "' title='" + escapedTooltip + "'>" + val.label + "</label>"; + } else { + urlConfigHtml += "<label for='qunit-urlconfig-" + escaped + + "' title='" + escapedTooltip + "'>" + val.label + + ": </label><select id='qunit-urlconfig-" + escaped + + "' name='" + escaped + "' title='" + escapedTooltip + "'><option></option>"; + + if ( QUnit.is( "array", val.value ) ) { + for ( j = 0; j < val.value.length; j++ ) { + escaped = escapeText( val.value[ j ] ); + urlConfigHtml += "<option value='" + escaped + "'" + + ( config[ val.id ] === val.value[ j ] ? + ( selection = true ) && " selected='selected'" : "" ) + + ">" + escaped + "</option>"; + } + } else { + for ( j in val.value ) { + if ( hasOwn.call( val.value, j ) ) { + urlConfigHtml += "<option value='" + escapeText( j ) + "'" + + ( config[ val.id ] === j ? + ( selection = true ) && " selected='selected'" : "" ) + + ">" + escapeText( val.value[ j ] ) + "</option>"; + } + } } + if ( config[ val.id ] && !selection ) { + escaped = escapeText( config[ val.id ] ); + urlConfigHtml += "<option value='" + escaped + + "' selected='selected' disabled='disabled'>" + escaped + "</option>"; + } + urlConfigHtml += "</select>"; } + } - for (i = n.length - 1; i > 0; i--) { - if (n[i].text != null && n[i - 1].text == null && n[i].row > 0 && o[n[i].row - 1].text == null && - n[i - 1] == o[n[i].row - 1]) { - n[i - 1] = { - text: n[i - 1], - row: n[i].row - 1 - }; - o[n[i].row - 1] = { - text: o[n[i].row - 1], - row: i - 1 - }; - } + return urlConfigHtml; +} + +// Handle "click" events on toolbar checkboxes and "change" for select menus. +// Updates the URL with the new state of `config.urlConfig` values. +function toolbarChanged() { + var updatedUrl, value, + field = this, + params = {}; + + // Detect if field is a select menu or a checkbox + if ( "selectedIndex" in field ) { + value = field.options[ field.selectedIndex ].value || undefined; + } else { + value = field.checked ? ( field.defaultValue || true ) : undefined; + } + + params[ field.name ] = value; + updatedUrl = setUrl( params ); + + if ( "hidepassed" === field.name && "replaceState" in window.history ) { + config[ field.name ] = value || false; + if ( value ) { + addClass( id( "qunit-tests" ), "hidepass" ); + } else { + removeClass( id( "qunit-tests" ), "hidepass" ); } - return { - o: o, - n: n - }; + // It is not necessary to refresh the whole page + window.history.replaceState( null, "", updatedUrl ); + } else { + window.location = updatedUrl; } +} - return function(o, n) { - o = o.replace(/\s+$/, ''); - n = n.replace(/\s+$/, ''); - var out = diff(o === "" ? [] : o.split(/\s+/), n === "" ? [] : n.split(/\s+/)); +function setUrl( params ) { + var key, + querystring = "?"; - var str = ""; - var i; + params = QUnit.extend( QUnit.extend( {}, QUnit.urlParams ), params ); - var oSpace = o.match(/\s+/g); - if (oSpace == null) { - oSpace = [" "]; + for ( key in params ) { + if ( hasOwn.call( params, key ) ) { + if ( params[ key ] === undefined ) { + continue; + } + querystring += encodeURIComponent( key ); + if ( params[ key ] !== true ) { + querystring += "=" + encodeURIComponent( params[ key ] ); + } + querystring += "&"; } - else { - oSpace.push(" "); + } + return location.protocol + "//" + location.host + + location.pathname + querystring.slice( 0, -1 ); +} + +function applyUrlParams() { + var selectedModule, + modulesList = id( "qunit-modulefilter" ), + filter = id( "qunit-filter-input" ).value; + + selectedModule = modulesList ? + decodeURIComponent( modulesList.options[ modulesList.selectedIndex ].value ) : + undefined; + + window.location = setUrl({ + module: ( selectedModule === "" ) ? undefined : selectedModule, + filter: ( filter === "" ) ? undefined : filter, + + // Remove testId filter + testId: undefined + }); +} + +function toolbarUrlConfigContainer() { + var urlConfigContainer = document.createElement( "span" ); + + urlConfigContainer.innerHTML = getUrlConfigHtml(); + addClass( urlConfigContainer, "qunit-url-config" ); + + // For oldIE support: + // * Add handlers to the individual elements instead of the container + // * Use "click" instead of "change" for checkboxes + addEvents( urlConfigContainer.getElementsByTagName( "input" ), "click", toolbarChanged ); + addEvents( urlConfigContainer.getElementsByTagName( "select" ), "change", toolbarChanged ); + + return urlConfigContainer; +} + +function toolbarLooseFilter() { + var filter = document.createElement( "form" ), + label = document.createElement( "label" ), + input = document.createElement( "input" ), + button = document.createElement( "button" ); + + addClass( filter, "qunit-filter" ); + + label.innerHTML = "Filter: "; + + input.type = "text"; + input.value = config.filter || ""; + input.name = "filter"; + input.id = "qunit-filter-input"; + + button.innerHTML = "Go"; + + label.appendChild( input ); + + filter.appendChild( label ); + filter.appendChild( button ); + addEvent( filter, "submit", function( ev ) { + applyUrlParams(); + + if ( ev && ev.preventDefault ) { + ev.preventDefault(); } - var nSpace = n.match(/\s+/g); - if (nSpace == null) { - nSpace = [" "]; + + return false; + }); + + return filter; +} + +function toolbarModuleFilterHtml() { + var i, + moduleFilterHtml = ""; + + if ( !modulesList.length ) { + return false; + } + + modulesList.sort(function( a, b ) { + return a.localeCompare( b ); + }); + + moduleFilterHtml += "<label for='qunit-modulefilter'>Module: </label>" + + "<select id='qunit-modulefilter' name='modulefilter'><option value='' " + + ( QUnit.urlParams.module === undefined ? "selected='selected'" : "" ) + + ">< All Modules ></option>"; + + for ( i = 0; i < modulesList.length; i++ ) { + moduleFilterHtml += "<option value='" + + escapeText( encodeURIComponent( modulesList[ i ] ) ) + "' " + + ( QUnit.urlParams.module === modulesList[ i ] ? "selected='selected'" : "" ) + + ">" + escapeText( modulesList[ i ] ) + "</option>"; + } + moduleFilterHtml += "</select>"; + + return moduleFilterHtml; +} + +function toolbarModuleFilter() { + var toolbar = id( "qunit-testrunner-toolbar" ), + moduleFilter = document.createElement( "span" ), + moduleFilterHtml = toolbarModuleFilterHtml(); + + if ( !toolbar || !moduleFilterHtml ) { + return false; + } + + moduleFilter.setAttribute( "id", "qunit-modulefilter-container" ); + moduleFilter.innerHTML = moduleFilterHtml; + + addEvent( moduleFilter.lastChild, "change", applyUrlParams ); + + toolbar.appendChild( moduleFilter ); +} + +function appendToolbar() { + var toolbar = id( "qunit-testrunner-toolbar" ); + + if ( toolbar ) { + toolbar.appendChild( toolbarUrlConfigContainer() ); + toolbar.appendChild( toolbarLooseFilter() ); + } +} + +function appendHeader() { + var header = id( "qunit-header" ); + + if ( header ) { + header.innerHTML = "<a href='" + + setUrl({ filter: undefined, module: undefined, testId: undefined }) + + "'>" + header.innerHTML + "</a> "; + } +} + +function appendBanner() { + var banner = id( "qunit-banner" ); + + if ( banner ) { + banner.className = ""; + } +} + +function appendTestResults() { + var tests = id( "qunit-tests" ), + result = id( "qunit-testresult" ); + + if ( result ) { + result.parentNode.removeChild( result ); + } + + if ( tests ) { + tests.innerHTML = ""; + result = document.createElement( "p" ); + result.id = "qunit-testresult"; + result.className = "result"; + tests.parentNode.insertBefore( result, tests ); + result.innerHTML = "Running...<br /> "; + } +} + +function storeFixture() { + var fixture = id( "qunit-fixture" ); + if ( fixture ) { + config.fixture = fixture.innerHTML; + } +} + +function appendUserAgent() { + var userAgent = id( "qunit-userAgent" ); + + if ( userAgent ) { + userAgent.innerHTML = ""; + userAgent.appendChild( + document.createTextNode( + "QUnit " + QUnit.version + "; " + navigator.userAgent + ) + ); + } +} + +function appendTestsList( modules ) { + var i, l, x, z, test, moduleObj; + + for ( i = 0, l = modules.length; i < l; i++ ) { + moduleObj = modules[ i ]; + + if ( moduleObj.name ) { + modulesList.push( moduleObj.name ); } - else { - nSpace.push(" "); + + for ( x = 0, z = moduleObj.tests.length; x < z; x++ ) { + test = moduleObj.tests[ x ]; + + appendTest( test.name, test.testId, moduleObj.name ); } + } +} + +function appendTest( name, testId, moduleName ) { + var title, rerunTrigger, testBlock, assertList, + tests = id( "qunit-tests" ); + + if ( !tests ) { + return; + } + + title = document.createElement( "strong" ); + title.innerHTML = getNameHtml( name, moduleName ); + + rerunTrigger = document.createElement( "a" ); + rerunTrigger.innerHTML = "Rerun"; + rerunTrigger.href = setUrl({ testId: testId }); + + testBlock = document.createElement( "li" ); + testBlock.appendChild( title ); + testBlock.appendChild( rerunTrigger ); + testBlock.id = "qunit-test-output-" + testId; + + assertList = document.createElement( "ol" ); + assertList.className = "qunit-assert-list"; + + testBlock.appendChild( assertList ); + + tests.appendChild( testBlock ); +} + +// HTML Reporter initialization and load +QUnit.begin(function( details ) { + var qunit = id( "qunit" ); + + // Fixture is the only one necessary to run without the #qunit element + storeFixture(); + + if ( qunit ) { + qunit.innerHTML = + "<h1 id='qunit-header'>" + escapeText( document.title ) + "</h1>" + + "<h2 id='qunit-banner'></h2>" + + "<div id='qunit-testrunner-toolbar'></div>" + + "<h2 id='qunit-userAgent'></h2>" + + "<ol id='qunit-tests'></ol>"; + } - if (out.n.length === 0) { - for (i = 0; i < out.o.length; i++) { - str += '<del>' + out.o[i] + oSpace[i] + "</del>"; + appendHeader(); + appendBanner(); + appendTestResults(); + appendUserAgent(); + appendToolbar(); + appendTestsList( details.modules ); + toolbarModuleFilter(); + + if ( qunit && config.hidepassed ) { + addClass( qunit.lastChild, "hidepass" ); + } +}); + +QUnit.done(function( details ) { + var i, key, + banner = id( "qunit-banner" ), + tests = id( "qunit-tests" ), + html = [ + "Tests completed in ", + details.runtime, + " milliseconds.<br />", + "<span class='passed'>", + details.passed, + "</span> assertions of <span class='total'>", + details.total, + "</span> passed, <span class='failed'>", + details.failed, + "</span> failed." + ].join( "" ); + + if ( banner ) { + banner.className = details.failed ? "qunit-fail" : "qunit-pass"; + } + + if ( tests ) { + id( "qunit-testresult" ).innerHTML = html; + } + + if ( config.altertitle && defined.document && document.title ) { + + // show ✖ for good, ✔ for bad suite result in title + // use escape sequences in case file gets loaded with non-utf-8-charset + document.title = [ + ( details.failed ? "\u2716" : "\u2714" ), + document.title.replace( /^[\u2714\u2716] /i, "" ) + ].join( " " ); + } + + // clear own sessionStorage items if all tests passed + if ( config.reorder && defined.sessionStorage && details.failed === 0 ) { + for ( i = 0; i < sessionStorage.length; i++ ) { + key = sessionStorage.key( i++ ); + if ( key.indexOf( "qunit-test-" ) === 0 ) { + sessionStorage.removeItem( key ); } } - else { - if (out.n[0].text == null) { - for (n = 0; n < out.o.length && out.o[n].text == null; n++) { - str += '<del>' + out.o[n] + oSpace[n] + "</del>"; - } - } + } - for (i = 0; i < out.n.length; i++) { - if (out.n[i].text == null) { - str += '<ins>' + out.n[i] + nSpace[i] + "</ins>"; - } - else { - var pre = ""; + // scroll back to top to show results + if ( config.scrolltop && window.scrollTo ) { + window.scrollTo( 0, 0 ); + } +}); - for (n = out.n[i].row + 1; n < out.o.length && out.o[n].text == null; n++) { - pre += '<del>' + out.o[n] + oSpace[n] + "</del>"; - } - str += " " + out.n[i].text + nSpace[i] + pre; - } +function getNameHtml( name, module ) { + var nameHtml = ""; + + if ( module ) { + nameHtml = "<span class='module-name'>" + escapeText( module ) + "</span>: "; + } + + nameHtml += "<span class='test-name'>" + escapeText( name ) + "</span>"; + + return nameHtml; +} + +QUnit.testStart(function( details ) { + var running, testBlock, bad; + + testBlock = id( "qunit-test-output-" + details.testId ); + if ( testBlock ) { + testBlock.className = "running"; + } else { + + // Report later registered tests + appendTest( details.name, details.testId, details.module ); + } + + running = id( "qunit-testresult" ); + if ( running ) { + bad = QUnit.config.reorder && defined.sessionStorage && + +sessionStorage.getItem( "qunit-test-" + details.module + "-" + details.name ); + + running.innerHTML = ( bad ? + "Rerunning previously failed test: <br />" : + "Running: <br />" ) + + getNameHtml( details.name, details.module ); + } + +}); + +QUnit.log(function( details ) { + var assertList, assertLi, + message, expected, actual, + testItem = id( "qunit-test-output-" + details.testId ); + + if ( !testItem ) { + return; + } + + message = escapeText( details.message ) || ( details.result ? "okay" : "failed" ); + message = "<span class='test-message'>" + message + "</span>"; + message += "<span class='runtime'>@ " + details.runtime + " ms</span>"; + + // pushFailure doesn't provide details.expected + // when it calls, it's implicit to also not show expected and diff stuff + // Also, we need to check details.expected existence, as it can exist and be undefined + if ( !details.result && hasOwn.call( details, "expected" ) ) { + expected = escapeText( QUnit.dump.parse( details.expected ) ); + actual = escapeText( QUnit.dump.parse( details.actual ) ); + message += "<table><tr class='test-expected'><th>Expected: </th><td><pre>" + + expected + + "</pre></td></tr>"; + + if ( actual !== expected ) { + message += "<tr class='test-actual'><th>Result: </th><td><pre>" + + actual + "</pre></td></tr>" + + "<tr class='test-diff'><th>Diff: </th><td><pre>" + + QUnit.diff( expected, actual ) + "</pre></td></tr>"; + } else { + if ( expected.indexOf( "[object Array]" ) !== -1 || + expected.indexOf( "[object Object]" ) !== -1 ) { + message += "<tr class='test-message'><th>Message: </th><td>" + + "Diff suppressed as the depth of object is more than current max depth (" + + QUnit.config.maxDepth + ").<p>Hint: Use <code>QUnit.dump.maxDepth</code> to " + + " run with a higher max depth or <a href='" + setUrl({ maxDepth: -1 }) + "'>" + + "Rerun</a> without max depth.</p></td></tr>"; } } - return str; - }; -}()); + if ( details.source ) { + message += "<tr class='test-source'><th>Source: </th><td><pre>" + + escapeText( details.source ) + "</pre></td></tr>"; + } + + message += "</table>"; + + // this occours when pushFailure is set and we have an extracted stack trace + } else if ( !details.result && details.source ) { + message += "<table>" + + "<tr class='test-source'><th>Source: </th><td><pre>" + + escapeText( details.source ) + "</pre></td></tr>" + + "</table>"; + } + + assertList = testItem.getElementsByTagName( "ol" )[ 0 ]; + + assertLi = document.createElement( "li" ); + assertLi.className = details.result ? "pass" : "fail"; + assertLi.innerHTML = message; + assertList.appendChild( assertLi ); +}); + +QUnit.testDone(function( details ) { + var testTitle, time, testItem, assertList, + good, bad, testCounts, skipped, + tests = id( "qunit-tests" ); -// for CommonJS enviroments, export everything -if ( typeof exports !== "undefined" || typeof require !== "undefined" ) { - extend(exports, QUnit); + if ( !tests ) { + return; + } + + testItem = id( "qunit-test-output-" + details.testId ); + + assertList = testItem.getElementsByTagName( "ol" )[ 0 ]; + + good = details.passed; + bad = details.failed; + + // store result when possible + if ( config.reorder && defined.sessionStorage ) { + if ( bad ) { + sessionStorage.setItem( "qunit-test-" + details.module + "-" + details.name, bad ); + } else { + sessionStorage.removeItem( "qunit-test-" + details.module + "-" + details.name ); + } + } + + if ( bad === 0 ) { + addClass( assertList, "qunit-collapsed" ); + } + + // testItem.firstChild is the test name + testTitle = testItem.firstChild; + + testCounts = bad ? + "<b class='failed'>" + bad + "</b>, " + "<b class='passed'>" + good + "</b>, " : + ""; + + testTitle.innerHTML += " <b class='counts'>(" + testCounts + + details.assertions.length + ")</b>"; + + if ( details.skipped ) { + testItem.className = "skipped"; + skipped = document.createElement( "em" ); + skipped.className = "qunit-skipped-label"; + skipped.innerHTML = "skipped"; + testItem.insertBefore( skipped, testTitle ); + } else { + addEvent( testTitle, "click", function() { + toggleClass( assertList, "qunit-collapsed" ); + }); + + testItem.className = bad ? "fail" : "pass"; + + time = document.createElement( "span" ); + time.className = "runtime"; + time.innerHTML = details.runtime + " ms"; + testItem.insertBefore( time, assertList ); + } +}); + +if ( defined.document ) { + if ( document.readyState === "complete" ) { + QUnit.load(); + } else { + addEvent( window, "load", QUnit.load ); + } +} else { + config.pageLoaded = true; + config.autorun = true; } -// get at whatever the global object is, like window in browsers -}( (function() {return this;}.call()) )); +})(); diff --git a/wqflask/wqflask/static/new/packages/ValidationPlugin/test/rules.js b/wqflask/wqflask/static/new/packages/ValidationPlugin/test/rules.js index 0b2f6d44..f19a8702 100755..100644 --- a/wqflask/wqflask/static/new/packages/ValidationPlugin/test/rules.js +++ b/wqflask/wqflask/static/new/packages/ValidationPlugin/test/rules.js @@ -1,56 +1,67 @@ module("rules"); test("rules() - internal - input", function() { - var element = $('#firstname'); - var v = $('#testForm1').validate(); + var element = $("#firstname"); + + $("#testForm1").validate(); + deepEqual( element.rules(), { required: true, minlength: 2 } ); }); test("rules(), ignore method:false", function() { - var element = $('#firstnamec'); - var v = $('#testForm1clean').validate({ + var element = $("#firstnamec"); + + $("#testForm1clean").validate({ rules: { - firstname: { required: false, minlength: 2 } + firstnamec: { required: false, minlength: 2 } } }); + deepEqual( element.rules(), { minlength: 2 } ); }); test("rules() HTML5 required (no value)", function() { - var element = $('#testForm11text1'); - var v = $('#testForm11').validate(); + var element = $("#testForm11text1"); + + $("#testForm11").validate(); + deepEqual( element.rules(), { required: true } ); }); test("rules() - internal - select", function() { - var element = $('#meal'); - var v = $('#testForm3').validate(); - deepEqual( element.rules(), {required: true} ); + var element = $("#meal"); + + $("#testForm3").validate(); + + deepEqual( element.rules(), { required: true } ); }); test("rules() - external", function() { - var element = $('#text1'); - var v = $('#form').validate({ + var element = $("#text1"); + + $("#form").validate({ rules: { - action: {date: true, min: 5} + action: { date: true, min: 5 } } }); - deepEqual( element.rules(), {date: true, min: 5} ); + + deepEqual( element.rules(), { date: true, min: 5 } ); }); test("rules() - external - complete form", function() { expect(1); - var methods = $.extend({}, $.validator.methods); - var messages = $.extend({}, $.validator.messages); + var methods = $.extend({}, $.validator.methods), + messages = $.extend({}, $.validator.messages), + v; $.validator.addMethod("verifyTest", function() { ok( true, "method executed" ); return true; }); - var v = $('#form').validate({ + v = $("#form").validate({ rules: { - action: {verifyTest: true} + action: { verifyTest: true } } }); v.form(); @@ -60,42 +71,46 @@ test("rules() - external - complete form", function() { }); test("rules() - internal - input", function() { - var element = $('#form8input'); - var v = $('#testForm8').validate(); - deepEqual( element.rules(), {required: true, number: true, rangelength: [2, 8]}); + var element = $("#form8input"); + + $("#testForm8").validate(); + + deepEqual( element.rules(), { required: true, number: true, rangelength: [ 2, 8 ] } ); }); test("rules(), merge min/max to range, minlength/maxlength to rangelength", function() { jQuery.validator.autoCreateRanges = true; - var v = $("#testForm1clean").validate({ + + $("#testForm1clean").validate({ rules: { - firstname: { - min: 5, - max: 12 + firstnamec: { + min: -15, + max: 0 }, lastname: { - minlength: 2, - maxlength: 8 + minlength: 0, + maxlength: 10 } } }); - deepEqual( $("#firstnamec").rules(), {range: [5, 12]}); - deepEqual( $("#lastnamec").rules(), {rangelength: [2, 8]} ); + deepEqual( $("#firstnamec").rules(), { range: [ -15, 0 ] } ); + deepEqual( $("#lastnamec").rules(), { rangelength: [ 0, 10 ] } ); + jQuery.validator.autoCreateRanges = false; }); -test("rules(), gurantee that required is at front", function() { +test("rules(), guarantee that required is at front", function() { $("#testForm1").validate(); var v = $("#v2").validate(); $("#subformRequired").validate(); function flatRules(element) { var result = []; - jQuery.each($(element).rules(), function(key, value) { result.push(key) }); + jQuery.each($(element).rules(), function(key) { result.push(key); }); return result.join(" "); } equal( "required minlength", flatRules("#firstname") ); - equal( "required maxlength minlength", flatRules("#v2-i6") ); + equal( "required minlength maxlength", flatRules("#v2-i6") ); equal( "required maxlength", flatRules("#co_name") ); QUnit.reset(); @@ -115,9 +130,10 @@ test("rules(), gurantee that required is at front", function() { test("rules(), evaluate dynamic parameters", function() { expect(2); - var v = $("#testForm1clean").validate({ + + $("#testForm1clean").validate({ rules: { - firstname: { + firstnamec: { min: function(element) { equal( $("#firstnamec")[0], element ); return 12; @@ -125,7 +141,8 @@ test("rules(), evaluate dynamic parameters", function() { } } }); - deepEqual( $("#firstnamec").rules(), {min:12}); + + deepEqual( $("#firstnamec").rules(), { min: 12 }); }); test("rules(), class and attribute combinations", function() { @@ -136,23 +153,25 @@ test("rules(), class and attribute combinations", function() { $.validator.addMethod("customMethod2", function() { return false; }, ""); - var v = $("#v2").validate({ + + $("#v2").validate({ rules: { - 'v2-i7': { + "v2-i7": { required: true, minlength: 2, customMethod: true } } }); + deepEqual( $("#v2-i1").rules(), { required: true }); deepEqual( $("#v2-i2").rules(), { required: true, email: true }); deepEqual( $("#v2-i3").rules(), { url: true }); deepEqual( $("#v2-i4").rules(), { required: true, minlength: 2 }); deepEqual( $("#v2-i5").rules(), { required: true, minlength: 2, maxlength: 5, customMethod1: "123" }); jQuery.validator.autoCreateRanges = true; - deepEqual( $("#v2-i5").rules(), { required: true, customMethod1: "123", rangelength: [2, 5] }); - deepEqual( $("#v2-i6").rules(), { required: true, customMethod2: true, rangelength: [2, 5] }); + deepEqual( $("#v2-i5").rules(), { required: true, customMethod1: "123", rangelength: [ 2, 5 ] }); + deepEqual( $("#v2-i6").rules(), { required: true, customMethod2: true, rangelength: [ 2, 5 ] }); jQuery.validator.autoCreateRanges = false; deepEqual( $("#v2-i7").rules(), { required: true, minlength: 2, customMethod: true }); @@ -164,33 +183,33 @@ test("rules(), class and attribute combinations", function() { test("rules(), dependency checks", function() { var v = $("#testForm1clean").validate({ - rules: { - firstname: { - min: { - param: 5, - depends: function(el) { - return /^a/.test($(el).val()); + rules: { + firstnamec: { + min: { + param: 5, + depends: function(el) { + return (/^a/).test($(el).val()); + } } - } - }, - lastname: { - max: { - param: 12 }, - email: { - depends: function() { return true; } + lastname: { + max: { + param: 12 + }, + email: { + depends: function() { return true; } + } } } - } - }); + }), + rules = $("#firstnamec").rules(); - var rules = $("#firstnamec").rules(); equal( 0, v.objectLength(rules) ); - $("#firstnamec").val('ab'); - deepEqual( $("#firstnamec").rules(), {min:5}); + $("#firstnamec").val("ab"); + deepEqual( $("#firstnamec").rules(), { min: 5 }); - deepEqual( $("#lastnamec").rules(), {max:12, email:true}); + deepEqual( $("#lastnamec").rules(), { max: 12, email: true }); }); test("rules(), add and remove", function() { @@ -198,16 +217,19 @@ test("rules(), add and remove", function() { return false; }, ""); $("#v2").validate(); - var removedAttrs = $("#v2-i5").removeClass("required").removeAttrs("minlength maxlength"); + $("#v2-i5").removeClass("required").removeAttr("minlength maxlength"); deepEqual( $("#v2-i5").rules(), { customMethod1: "123" }); - $("#v2-i5").addClass("required").attr(removedAttrs); + $("#v2-i5").addClass("required").attr({ + minlength: 2, + maxlength: 5 + }); deepEqual( $("#v2-i5").rules(), { required: true, minlength: 2, maxlength: 5, customMethod1: "123" }); - $("#v2-i5").addClass("email").attr({min: 5}); + $("#v2-i5").addClass("email").attr({ min: 5 }); deepEqual( $("#v2-i5").rules(), { required: true, email: true, minlength: 2, maxlength: 5, min: 5, customMethod1: "123" }); - $("#v2-i5").removeClass("required email").removeAttrs("minlength maxlength customMethod1 min"); + $("#v2-i5").removeClass("required email").removeAttr("minlength maxlength customMethod1 min"); deepEqual( $("#v2-i5").rules(), {}); delete $.validator.methods.customMethod1; @@ -215,14 +237,16 @@ test("rules(), add and remove", function() { }); test("rules(), add and remove static rules", function() { - var v = $("#testForm1clean").validate({ + + $("#testForm1clean").validate({ rules: { - firstname: "required date" + firstnamec: "required date" } }); + deepEqual( $("#firstnamec").rules(), { required: true, date: true } ); - $("#firstnamec").rules("remove", "date") + $("#firstnamec").rules("remove", "date"); deepEqual( $("#firstnamec").rules(), { required: true } ); $("#firstnamec").rules("add", "email"); deepEqual( $("#firstnamec").rules(), { required: true, email: true } ); @@ -236,7 +260,6 @@ test("rules(), add and remove static rules", function() { $("#firstnamec").rules("add", "required email"); deepEqual( $("#firstnamec").rules(), { required: true, email: true } ); - deepEqual( $("#lastnamec").rules(), {} ); $("#lastnamec").rules("add", "required"); $("#lastnamec").rules("add", { @@ -244,7 +267,6 @@ test("rules(), add and remove static rules", function() { }); deepEqual( $("#lastnamec").rules(), { required: true, minlength: 2 } ); - var removedRules = $("#lastnamec").rules("remove", "required email"); deepEqual( $("#lastnamec").rules(), { minlength: 2 } ); $("#lastnamec").rules("add", removedRules); @@ -255,7 +277,7 @@ test("rules(), add messages", function() { $("#firstnamec").attr("title", null); var v = $("#testForm1clean").validate({ rules: { - firstname: "required" + firstnamec: "required" } }); $("#testForm1clean").valid(); @@ -270,4 +292,16 @@ test("rules(), add messages", function() { $("#firstnamec").valid(); deepEqual( v.errorList[0] && v.errorList[0].message, "required" ); + + $("#firstnamec").val("test"); + $("#firstnamec").valid(); + equal(v.errorList.length, 0); +}); + +test( "rules(), rangelength attribute as array", function() { + $("#testForm13").validate(); + deepEqual( $("#cars-select").rules(), { + required: true, + rangelength: [ 2, 3 ] + }); }); diff --git a/wqflask/wqflask/static/new/packages/ValidationPlugin/test/selects/index.html b/wqflask/wqflask/static/new/packages/ValidationPlugin/test/selects/index.html deleted file mode 100755 index fae8127a..00000000 --- a/wqflask/wqflask/static/new/packages/ValidationPlugin/test/selects/index.html +++ /dev/null @@ -1,436 +0,0 @@ -<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> -<html xmlns="http://www.w3.org/1999/xhtml"> -<head> -<meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> - -<title>Fun with jQuery</title> - -<script src="http://www.google.com/jsapi"></script> -<script> - google.load("jquery", "1"); -</script> - -<script type="text/javascript"> - -$.fn.options = function(selector) { - return this.each(function() { - function container(select) { - if (select.next().is(".option-container")) { - return $(select).next(); - } - return $('<select class="option-container" />').append(select.children()).insertAfter(select).hide(); - } - var container = container($(this)); - $(this).empty().append(container.children(selector).clone()); - }); -} - -$(document).ready(function(){ - - $("#State").hide() - - $("#Country").change(function() { - var selected = this.options[this.selectedIndex].value; - if (selected == "US") { - $("#State").show().options(".state"); - } else if (selected == "CA") { - $("#State").show().options(".province"); - } else { - $("#State").hide(); - } - }).change(); - - -}); -</script> - - - -</head> - -<body> -Mission: - -<xmp> -CODE - -</xmp> - - - <select size="1" id="Country" name="country"> - <option value="">Select One</option> - - <option value="US" selected="selected">United States</option> - <option value="CA">Canada</option> - <option value="">----------</option> - <option value="AF">Afghanistan</option> - <option value="AL">Albania</option> - <option value="DZ">Algeria</option> - - <option value="AS">American Samoa</option> - <option value="AD">Andorra</option> - <option value="AO">Angola</option> - <option value="AI">Anguilla</option> - <option value="AQ">Antarctica</option> - <option value="AG">Antigua and Barbuda</option> - - <option value="AR">Argentina</option> - <option value="AM">Armenia</option> - <option value="AW">Aruba</option> - <option value="AU">Australia</option> - <option value="AT">Austria</option> - <option value="AZ">Azerbaidjan</option> - - <option value="BS">Bahamas</option> - <option value="BH">Bahrain</option> - <option value="BD">Bangladesh</option> - <option value="BB">Barbados</option> - <option value="BY">Belarus</option> - <option value="BE">Belgium</option> - - <option value="BZ">Belize</option> - <option value="BJ">Benin</option> - <option value="BM">Bermuda</option> - <option value="BT">Bhutan</option> - <option value="BO">Bolivia</option> - <option value="BA">Bosnia-Herzegovina</option> - - <option value="BW">Botswana</option> - <option value="BV">Bouvet Island</option> - <option value="BR">Brazil</option> - <option value="IO">British Indian Ocean Territory</option> - <option value="BN">Brunei Darussalam</option> - <option value="BG">Bulgaria</option> - - <option value="BF">Burkina Faso</option> - <option value="BI">Burundi</option> - <option value="KH">Cambodia</option> - <option value="CM">Cameroon</option> - <option value="CV">Cape Verde</option> - <option value="KY">Cayman Islands</option> - - <option value="CF">Central African Republic</option> - <option value="TD">Chad</option> - <option value="CL">Chile</option> - <option value="CN">China</option> - <option value="CX">Christmas Island</option> - <option value="CC">Cocos (Keeling) Islands</option> - - <option value="CO">Colombia</option> - <option value="KM">Comoros</option> - <option value="CG">Congo</option> - <option value="CK">Cook Islands</option> - <option value="CR">Costa Rica</option> - <option value="HR">Croatia</option> - - <option value="CU">Cuba</option> - <option value="CY">Cyprus</option> - <option value="CZ">Czech Republic</option> - <option value="DK">Denmark</option> - <option value="DJ">Djibouti</option> - <option value="DM">Dominica</option> - - <option value="DO">Dominican Republic</option> - <option value="TP">East Timor</option> - <option value="EC">Ecuador</option> - <option value="EG">Egypt</option> - <option value="SV">El Salvador</option> - <option value="GQ">Equatorial Guinea</option> - - <option value="ER">Eritrea</option> - <option value="EE">Estonia</option> - <option value="ET">Ethiopia</option> - <option value="FK">Falkland Islands</option> - <option value="FO">Faroe Islands</option> - <option value="FJ">Fiji</option> - - <option value="FI">Finland</option> - <option value="CS">Former Czechoslovakia</option> - <option value="SU">Former USSR</option> - <option value="FR">France</option> - <option value="FX">France (European Territory)</option> - <option value="GF">French Guyana</option> - - <option value="TF">French Southern Territories</option> - <option value="GA">Gabon</option> - <option value="GM">Gambia</option> - <option value="GE">Georgia</option> - <option value="DE">Germany</option> - <option value="GH">Ghana</option> - - <option value="GI">Gibraltar</option> - <option value="GB">Great Britain</option> - <option value="GR">Greece</option> - <option value="GL">Greenland</option> - <option value="GD">Grenada</option> - <option value="GP">Guadeloupe (French)</option> - - <option value="GU">Guam (USA)</option> - <option value="GT">Guatemala</option> - <option value="GN">Guinea</option> - <option value="GW">Guinea Bissau</option> - <option value="GY">Guyana</option> - <option value="HT">Haiti</option> - - <option value="HM">Heard and McDonald Islands</option> - <option value="HN">Honduras</option> - <option value="HK">Hong Kong</option> - <option value="HU">Hungary</option> - <option value="IS">Iceland</option> - <option value="IN">India</option> - - <option value="ID">Indonesia</option> - <option value="INT">International</option> - <option value="IR">Iran</option> - <option value="IQ">Iraq</option> - <option value="IE">Ireland</option> - <option value="IL">Israel</option> - - <option value="IT">Italy</option> - <option value="CI">Ivory Coast (Cote D'Ivoire)</option> - <option value="JM">Jamaica</option> - <option value="JP">Japan</option> - <option value="JO">Jordan</option> - <option value="KZ">Kazakhstan</option> - - <option value="KE">Kenya</option> - <option value="KI">Kiribati</option> - <option value="KW">Kuwait</option> - <option value="KG">Kyrgyzstan</option> - <option value="LA">Laos</option> - <option value="LV">Latvia</option> - - <option value="LB">Lebanon</option> - <option value="LS">Lesotho</option> - <option value="LR">Liberia</option> - <option value="LY">Libya</option> - <option value="LI">Liechtenstein</option> - <option value="LT">Lithuania</option> - - <option value="LU">Luxembourg</option> - <option value="MO">Macau</option> - <option value="MK">Macedonia</option> - <option value="MG">Madagascar</option> - <option value="MW">Malawi</option> - <option value="MY">Malaysia</option> - - <option value="MV">Maldives</option> - <option value="ML">Mali</option> - <option value="MT">Malta</option> - <option value="MH">Marshall Islands</option> - <option value="MQ">Martinique (French)</option> - <option value="MR">Mauritania</option> - - <option value="MU">Mauritius</option> - <option value="YT">Mayotte</option> - <option value="MX">Mexico</option> - <option value="FM">Micronesia</option> - <option value="MD">Moldavia</option> - <option value="MC">Monaco</option> - - <option value="MN">Mongolia</option> - <option value="MS">Montserrat</option> - <option value="MA">Morocco</option> - <option value="MZ">Mozambique</option> - <option value="MM">Myanmar</option> - <option value="NA">Namibia</option> - - <option value="NR">Nauru</option> - <option value="NP">Nepal</option> - <option value="NL">Netherlands</option> - <option value="AN">Netherlands Antilles</option> - <option value="NT">Neutral Zone</option> - <option value="NC">New Caledonia (French)</option> - - <option value="NZ">New Zealand</option> - <option value="NI">Nicaragua</option> - <option value="NE">Niger</option> - <option value="NG">Nigeria</option> - <option value="NU">Niue</option> - <option value="NF">Norfolk Island</option> - - <option value="KP">North Korea</option> - <option value="MP">Northern Mariana Islands</option> - <option value="NO">Norway</option> - <option value="OM">Oman</option> - <option value="PK">Pakistan</option> - <option value="PW">Palau</option> - - <option value="PA">Panama</option> - <option value="PG">Papua New Guinea</option> - <option value="PY">Paraguay</option> - <option value="PE">Peru</option> - <option value="PH">Philippines</option> - <option value="PN">Pitcairn Island</option> - - <option value="PL">Poland</option> - <option value="PF">Polynesia (French)</option> - <option value="PT">Portugal</option> - <option value="PR">Puerto Rico</option> - <option value="QA">Qatar</option> - <option value="RE">Reunion (French)</option> - - <option value="RO">Romania</option> - <option value="RU">Russian Federation</option> - <option value="RW">Rwanda</option> - <option value="GS">S. Georgia & S. Sandwich Isls.</option> - <option value="SH">Saint Helena</option> - <option value="KN">Saint Kitts & Nevis Anguilla</option> - - <option value="LC">Saint Lucia</option> - <option value="PM">Saint Pierre and Miquelon</option> - <option value="ST">Saint Tome (Sao Tome) and Principe</option> - <option value="VC">Saint Vincent & Grenadines</option> - <option value="WS">Samoa</option> - <option value="SM">San Marino</option> - - <option value="SA">Saudi Arabia</option> - <option value="SN">Senegal</option> - <option value="SC">Seychelles</option> - <option value="SL">Sierra Leone</option> - <option value="SG">Singapore</option> - <option value="SK">Slovak Republic</option> - - <option value="SI">Slovenia</option> - <option value="SB">Solomon Islands</option> - <option value="SO">Somalia</option> - <option value="ZA">South Africa</option> - <option value="KR">South Korea</option> - <option value="ES">Spain</option> - - <option value="LK">Sri Lanka</option> - <option value="SD">Sudan</option> - <option value="SR">Suriname</option> - <option value="SJ">Svalbard and Jan Mayen Islands</option> - <option value="SZ">Swaziland</option> - <option value="SE">Sweden</option> - - <option value="CH">Switzerland</option> - <option value="SY">Syria</option> - <option value="TJ">Tadjikistan</option> - <option value="TW">Taiwan</option> - <option value="TZ">Tanzania</option> - <option value="TH">Thailand</option> - - <option value="TG">Togo</option> - <option value="TK">Tokelau</option> - <option value="TO">Tonga</option> - <option value="TT">Trinidad and Tobago</option> - <option value="TN">Tunisia</option> - <option value="TR">Turkey</option> - - <option value="TM">Turkmenistan</option> - <option value="TC">Turks and Caicos Islands</option> - <option value="TV">Tuvalu</option> - <option value="UG">Uganda</option> - <option value="UA">Ukraine</option> - <option value="AE">United Arab Emirates</option> - - <option value="GB">United Kingdom</option> - <option value="UY">Uruguay</option> - <option value="MIL">USA Military</option> - <option value="UM">USA Minor Outlying Islands</option> - <option value="UZ">Uzbekistan</option> - <option value="VU">Vanuatu</option> - - <option value="VA">Vatican City State</option> - <option value="VE">Venezuela</option> - <option value="VN">Vietnam</option> - <option value="VG">Virgin Islands (British)</option> - <option value="VI">Virgin Islands (USA)</option> - <option value="WF">Wallis and Futuna Islands</option> - - <option value="EH">Western Sahara</option> - <option value="YE">Yemen</option> - <option value="YU">Yugoslavia</option> - <option value="ZR">Zaire</option> - <option value="ZM">Zambia</option> - <option value="ZW">Zimbabwe</option> - - </select> -<br /> - -<select id="State" name="State"> - - <option value="" class="selectone">Select One</option> - <option value="AB" class="province">Alberta</option> - <option value="BC" class="province">British Columbia</option> - <option value="MB" class="province">Manitoba</option> - - <option value="NB" class="province">New Brunswick</option> - <option value="NF" class="province">Newfoundland</option> - <option value="NT" class="province">Northwest Territories</option> - <option value="NS" class="province">Nova Scotia</option> - <option value="NU" class="province">Nunavut</option> - <option value="ON" class="province">Ontario</option> - - <option value="PE" class="province">Prince Edward Island</option> - <option value="QC" class="province">Quebec</option> - <option value="SK" class="province">Saskatchewan</option> - <option value="YT" class="province">Yukon Territory</option> - - <option value="AK" class="state">Alaska</option> - <option value="AL" class="state">Alabama</option> - - <option value="AR" class="state">Arkansas</option> - <option value="AZ" class="state">Arizona</option> - <option value="CA" class="state">California</option> - <option value="CO" class="state">Colorado</option> - <option value="CT" class="state">Connecticut</option> - <option value="DC" class="state">District of Columbia</option> - - <option value="DE" class="state">Delaware</option> - <option value="FL" class="state">Florida</option> - <option value="GA" class="state">Georgia</option> - <option value="HI" class="state">Hawaii</option> - <option value="IA" class="state">Iowa</option> - <option value="ID" class="state">Idaho</option> - - <option value="IL" class="state">Illinois</option> - <option value="IN" class="state">Indiana</option> - <option value="KS" class="state">Kansas</option> - <option value="KY" class="state">Kentucky</option> - <option value="LA" class="state">Louisiana</option> - <option value="MA" class="state">Massachusetts</option> - - <option value="MD" class="state">Maryland</option> - <option value="ME" class="state">Maine</option> - <option value="MI" class="state">Michigan</option> - <option value="MN" class="state">Minnesota</option> - <option value="MO" class="state">Missouri</option> - <option value="MS" class="state">Mississippi</option> - - <option value="MT" class="state">Montana</option> - <option value="NC" class="state">North Carolina</option> - <option value="ND" class="state">North Dakota</option> - <option value="NE" class="state">Nebraska</option> - <option value="NH" class="state">New Hampshire</option> - <option value="NJ" class="state">New Jersey</option> - - <option value="NM" class="state">New Mexico</option> - <option value="NV" class="state">Nevada</option> - <option value="NY" class="state">New York</option> - <option value="OH" class="state">Ohio</option> - <option value="OK" class="state">Oklahoma</option> - <option value="OR" class="state">Oregon</option> - - <option value="PA" class="state">Pennsylvania</option> - <option value="PR" class="state">Puerto Rico</option> - <option value="RI" class="state">Rhode Island</option> - <option value="SC" class="state">South Carolina</option> - <option value="SD" class="state">South Dakota</option> - <option value="TN" class="state">Tennessee</option> - - <option value="TX" class="state">Texas</option> - <option value="UT" class="state">Utah</option> - <option value="VA" class="state">Virginia</option> - <option value="VT" class="state">Vermont</option> - <option value="WA" class="state">Washington</option> - <option value="WI" class="state">Wisconsin</option> - - <option value="WV" class="state">West Virginia</option> - <option value="WY" class="state">Wyoming</option> -</select> - -</body> -</html> diff --git a/wqflask/wqflask/static/new/packages/ValidationPlugin/test/tabs.html b/wqflask/wqflask/static/new/packages/ValidationPlugin/test/tabs.html deleted file mode 100755 index 06f1022c..00000000 --- a/wqflask/wqflask/static/new/packages/ValidationPlugin/test/tabs.html +++ /dev/null @@ -1,78 +0,0 @@ -<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> -<html xmlns="http://www.w3.org/1999/xhtml"> -<head> -<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1" /> -<title>Test for jQuery validate() plugin</title> - -<link rel="stylesheet" type="text/css" media="screen" href="../demo/css/screen.css" /> -<link rel="stylesheet" href="../../../themes/flora/flora.all.css" type="text/css" media="screen" title="Flora (Default)"> - -<script src="../lib/jquery.js" type="text/javascript"></script> -<script src="../../../ui/current/ui.tabs.js" type="text/javascript"></script> -<script type="text/javascript" src="../lib/jquery.metadata.js"></script> -<script type="text/javascript" src="../jquery.validate.js"></script> -<script src="firebug/firebug.js" type="text/javascript"></script> - -<script type="text/javascript"> - -$().ready(function() { - $("#commentForm").validate({debug:true}); - $("#example > ul").tabs(); -}); -</script> - -<style type="text/css"> -form.cmxform { width: 470px; } -</style> - -</head> -<body> - - <form class="cmxform" id="commentForm" method="get" action=""> - - <div id="example" class="flora"> - <ul> - - <li><a href="#fragment-1"><span>One</span></a></li> - <li><a href="#fragment-2"><span>Two</span></a></li> - <li><a href="#fragment-3"><span>Three</span></a></li> - </ul> - <div id="fragment-1"> - <fieldset> - <legend>A simple comment form with submit validation and default messages</legend> - <p> - <label for="cname">Name (required, at least 2 characters)</label> - <input id="cname" name="name" class="some other styles {required:true,minLength:2}" /> - <p> - <label for="cemail">E-Mail (required)</label> - <input id="cemail" name="email" class="{required:true,email:true}" /> - </p> - <p> - <label for="curl">URL (optional)</label> - <input id="curl" name="url" class="{url:true}" value="" /> - </p> - <p> - <label for="ccomment">Your comment (required)</label> - <textarea id="ccomment" name="comment" class="{required:true}"></textarea> - </p> - </fieldset> - - </div> - <div id="fragment-2"> - Lorem ipsum dolor sit amet, consectetuer adipiscing elit, sed diam nonummy nibh euismod tincidunt ut laoreet dolore magna aliquam erat volutpat. - Lorem ipsum dolor sit amet, consectetuer adipiscing elit, sed diam nonummy nibh euismod tincidunt ut laoreet dolore magna aliquam erat volutpat. - </div> - <div id="fragment-3"> - Lorem ipsum dolor sit amet, consectetuer adipiscing elit, sed diam nonummy nibh euismod tincidunt ut laoreet dolore magna aliquam erat volutpat. - Lorem ipsum dolor sit amet, consectetuer adipiscing elit, sed diam nonummy nibh euismod tincidunt ut laoreet dolore magna aliquam erat volutpat. - Lorem ipsum dolor sit amet, consectetuer adipiscing elit, sed diam nonummy nibh euismod tincidunt ut laoreet dolore magna aliquam erat volutpat. - </div> - </div> - <p> - <input class="submit" type="submit" value="Submit"/> - </p> - - </form> - -</body> -</html> diff --git a/wqflask/wqflask/static/new/packages/ValidationPlugin/test/test.js b/wqflask/wqflask/static/new/packages/ValidationPlugin/test/test.js index cc384d19..3cb08cae 100755..100644 --- a/wqflask/wqflask/static/new/packages/ValidationPlugin/test/test.js +++ b/wqflask/wqflask/static/new/packages/ValidationPlugin/test/test.js @@ -1,306 +1,442 @@ -window.sessionStorage && sessionStorage.clear(); +if ( window.sessionStorage ) { + sessionStorage.clear(); +} jQuery.validator.defaults.debug = true; $.mockjaxSettings.log = $.noop; $.mockjax({ url: "form.php?user=Peter&password=foobar", - responseText: 'Hi Peter, welcome back.', + responseText: "Hi Peter, welcome back.", responseStatus: 200, responseTime: 1 }); + $.mockjax({ url: "users.php", - data: { username: /Peter2?|asdf/}, - responseText: 'false', + data: { + username: /Peter2?|asdf/ + }, + responseText: "false", responseStatus: 200, responseTime: 1 }); + $.mockjax({ url: "users2.php", - data: { username: "asdf"}, - responseText: '"asdf is already taken, please try something else"', + data: { + username: "asdf" + }, + responseText: "\"asdf is already taken, please try something else\"", responseStatus: 200, responseTime: 1 }); + $.mockjax({ url: "echo.php", - response: function(data) { - this.responseText = JSON.stringify(data.data); + response: function( data ) { + this.responseText = JSON.stringify( data.data ); }, responseTime: 100 }); -module("validator"); +// Asserts that there is a visible error with the given text for the specified element +QUnit.assert.hasError = function( element, text, message ) { + var errors = $( element ).closest( "form" ).validate().errorsFor( element[ 0 ] ), + actual = ( errors.length === 1 && errors.is( ":visible" ) ) ? errors.text() : ""; + QUnit.push( actual, actual, text, message ); +}; + +// Asserts that there is no visible error for the given element +QUnit.assert.noErrorFor = function( element, message ) { + var errors = $( element ).closest( "form" ).validate().errorsFor( element[ 0 ] ), + hidden = ( errors.length === 0 ) || (errors.is( ":hidden" ) && ( errors.text() === "" ) ); + QUnit.push( hidden, hidden, true, message ); +}; + +module( "validator" ); + +test( "Constructor", function() { + var v1 = $( "#testForm1" ).validate(), + v2 = $( "#testForm1" ).validate(); -test("Constructor", function() { - var v1 = $("#testForm1").validate(); - var v2 = $("#testForm1").validate(); equal( v1, v2, "Calling validate() multiple times must return the same validator instance" ); equal( v1.elements().length, 3, "validator elements" ); }); -test("validate() without elements, with non-form elements", 0, function() { - $("#doesn'texist").validate(); +test( "validate() without elements, with non-form elements", 0, function() { + $( "#doesntexist" ).validate(); }); -test("valid() plugin method", function() { - var form = $("#userForm"); +test( "valid() plugin method", function() { + var form = $( "#userForm" ), + input = $( "#username" ); + form.validate(); ok ( !form.valid(), "Form isn't valid yet" ); - var input = $("#username"); ok ( !input.valid(), "Input isn't valid either" ); - input.val("Hello world"); + + input.val( "Hello world" ); ok ( form.valid(), "Form is now valid" ); ok ( input.valid(), "Input is valid, too" ); }); -test("valid() plugin method", function() { - var form = $("#testForm1"); - form.validate(); - var inputs = form.find("input"); +test( "valid() plugin method, multiple inputs", function() { + var form = $( "#testForm1" ), + validator = form.validate(), + inputs = form.find( "input" ); + ok( !inputs.valid(), "all invalid" ); - inputs.not(":first").val("ok"); - ok( !inputs.valid(), "just one invalid" ); - inputs.val("ok"); - ok( inputs.valid(), "all valid" ); + inputs.not( ":first" ).val( "ok" ); + equal( validator.numberOfInvalids(), 2 ); + strictEqual( inputs.valid(), false, "just one invalid" ); + inputs.val( "ok" ); + strictEqual( inputs.valid(), true, "all valid" ); }); -test("valid() plugin method, special handling for checkable groups", function() { +test( "valid() plugin method, special handling for checkable groups", function() { // rule is defined on first checkbox, must apply to others, too - var checkable = $("#checkable2"); + var checkable = $( "#checkable2" ); ok( !checkable.valid(), "must be invalid, not checked yet" ); - checkable.attr("checked", true); + checkable.attr( "checked", true ); ok( checkable.valid(), "valid, is now checked" ); - checkable.attr("checked", false); + checkable.attr( "checked", false ); ok( !checkable.valid(), "invalid again" ); - $("#checkable3").attr("checked", true); + $( "#checkable3" ).attr( "checked", true ); ok( checkable.valid(), "valid, third box is checked" ); }); -test("addMethod", function() { +test( "valid() ???", function() { + expect( 4 ); + var errorList = [ + { + name: "meal", + message: "foo", + element: $( "#meal" )[ 0 ] + } + ], + v = $( "#testForm3" ).validate(); + + ok( v.valid(), "No errors, must be valid" ); + v.errorList = errorList; + ok( !v.valid(), "One error, must be invalid" ); + QUnit.reset(); + v = $( "#testForm3" ).validate({ + submitHandler: function() { + ok( false, "Submit handler was called" ); + } + }); + ok( v.valid(), "No errors, must be valid and returning true, even with the submit handler" ); + v.errorList = errorList; + ok( !v.valid(), "One error, must be invalid, no call to submit handler" ); +}); + +test( "valid(), ignores ignored elements", function() { + $( "#testForm1clean" ).validate({ + ignore: "#firstnamec", + rules: { + firstnamec: "required" + } + }); + ok( $( "#firstnamec" ).valid() ); +}); + +test( "addMethod", function() { expect( 3 ); - $.validator.addMethod("hi", function(value) { - return value == "hi"; - }, "hi me too"); + $.validator.addMethod( "hi", function( value ) { + return value === "hi"; + }, "hi me too" ); var method = $.validator.methods.hi, - e = $('#text1')[0]; - ok( !method(e.value, e), "Invalid" ); + e = $( "#text1" )[ 0 ]; + ok( !method( e.value, e ), "Invalid" ); e.value = "hi"; - ok( method(e.value, e), "Invalid" ); - ok( jQuery.validator.messages.hi == "hi me too", "Check custom message" ); + ok( method( e.value, e ), "Invalid" ); + ok( jQuery.validator.messages.hi === "hi me too", "Check custom message" ); }); -test("addMethod2", function() { +test( "addMethod2", function() { expect( 4 ); - $.validator.addMethod("complicatedPassword", function(value, element, param) { - return this.optional(element) || /\D/.test(value) && /\d/.test(value) - }, "Your password must contain at least one number and one letter"); - var v = jQuery("#form").validate({ - rules: { - action: { complicatedPassword: true } - } - }); - var rule = $.validator.methods.complicatedPassword, - e = $('#text1')[0]; + $.validator.addMethod( "complicatedPassword", function( value, element ) { + return this.optional( element ) || /\D/.test( value ) && /\d/.test( value ); + }, "Your password must contain at least one number and one letter" ); + var v = jQuery( "#form" ).validate({ + rules: { + action: { complicatedPassword: true } + } + }), + e = $( "#text1" )[ 0 ]; + e.value = ""; - strictEqual( v.element(e), true, "Rule is optional, valid" ); + strictEqual( v.element( e ), true, "Rule is optional, valid" ); equal( 0, v.size() ); e.value = "ko"; - ok( !v.element(e), "Invalid, doesn't contain one of the required characters" ); + ok( !v.element( e ), "Invalid, doesn't contain one of the required characters" ); e.value = "ko1"; - ok( v.element(e) ); + ok( v.element( e ) ); }); -test("form(): simple", function() { +test( "form(): simple", function() { expect( 2 ); - var form = $('#testForm1')[0]; - var v = $(form).validate(); - ok( !v.form(), 'Invalid form' ); - $('#firstname').val("hi"); - $('#lastname').val("hi"); - ok( v.form(), 'Valid form' ); + var form = $( "#testForm1" )[ 0 ], + v = $( form ).validate(); + + ok( !v.form(), "Invalid form" ); + $( "#firstname" ).val( "hi" ); + $( "#lastname" ).val( "hi" ); + ok( v.form(), "Valid form" ); }); -test("form(): checkboxes: min/required", function() { +test( "form(): checkboxes: min/required", function() { expect( 3 ); - var form = $('#testForm6')[0]; - var v = $(form).validate(); - ok( !v.form(), 'Invalid form' ); - $('#form6check1').attr("checked", true); - ok( !v.form(), 'Invalid form' ); - $('#form6check2').attr("checked", true); - ok( v.form(), 'Valid form' ); + var form = $( "#testForm6" )[ 0 ], + v = $( form ).validate(); + + ok( !v.form(), "Invalid form" ); + $( "#form6check1" ).attr( "checked", true ); + ok( !v.form(), "Invalid form" ); + $( "#form6check2" ).attr( "checked", true ); + ok( v.form(), "Valid form" ); }); -test("form(): radio buttons: required", function () { +test( "form(): radio buttons: required", function() { expect( 6 ); - var form = $('#testForm10')[0]; + var form = $( "#testForm10" )[ 0 ], + v = $( form ).validate({ + rules: { + testForm10Radio: "required" + } + }); - var v = $(form).validate({ rules: { testForm10Radio: "required"} }); - ok(!v.form(), 'Invalid Form'); - equal($('#testForm10Radio1').attr('class'), 'error'); - equal($('#testForm10Radio2').attr('class'), 'error'); + ok(!v.form(), "Invalid Form" ); + equal($( "#testForm10Radio1" ).attr( "class" ), "error" ); + equal($( "#testForm10Radio2" ).attr( "class" ), "error" ); - $('#testForm10Radio2').attr("checked", true); - ok(v.form(), 'Valid form'); + $( "#testForm10Radio2" ).attr( "checked", true ); + ok( v.form(), "Valid form" ); - equal($('#testForm10Radio1').attr('class'), 'valid'); - equal($('#testForm10Radio2').attr('class'), 'valid'); + equal($( "#testForm10Radio1" ).attr( "class" ), "valid" ); + equal($( "#testForm10Radio2" ).attr( "class" ), "valid" ); }); -test("form(): selects: min/required", function() { +test( "form(): selects: min/required", function() { expect( 3 ); - var form = $('#testForm7')[0]; - var v = $(form).validate(); - ok( !v.form(), 'Invalid form' ); - $("#optionxa").attr("selected", true); - ok( !v.form(), 'Invalid form' ); - $("#optionxb").attr("selected", true); - ok( v.form(), 'Valid form' ); + var form = $( "#testForm7" )[ 0 ], + v = $( form ).validate(); + + ok( !v.form(), "Invalid form" ); + $( "#optionxa" ).attr( "selected", true ); + ok( !v.form(), "Invalid form" ); + $( "#optionxb" ).attr( "selected", true ); + ok( v.form(), "Valid form" ); }); -test("form(): with equalTo", function() { +test( "form(): with equalTo", function() { expect( 2 ); - var form = $('#testForm5')[0]; - var v = $(form).validate(); - ok( !v.form(), 'Invalid form' ); - $('#x1, #x2').val("hi"); - ok( v.form(), 'Valid form' ); + var form = $( "#testForm5" )[ 0 ], + v = $( form ).validate(); + + ok( !v.form(), "Invalid form" ); + $( "#x1, #x2" ).val( "hi" ); + ok( v.form(), "Valid form" ); }); -test("form(): with equalTo and onfocusout=false", function() { +test( "form(): with equalTo and onfocusout=false", function() { expect( 4 ); - var form = $('#testForm5')[0]; - var v = $(form).validate({ - onfocusout: false, - showErrors: function() { - ok(true, 'showErrors should only be called twice'); - this.defaultShowErrors(); - } - }); - $('#x1, #x2').val("hi"); - ok( v.form(), 'Valid form' ); - $('#x2').val('not equal').blur(); - ok( !v.form(), 'Invalid form' ); -}); + var form = $( "#testForm5" )[ 0 ], + v = $( form ).validate({ + onfocusout: false, + showErrors: function() { + ok( true, "showErrors should only be called twice" ); + this.defaultShowErrors(); + } + }); + $( "#x1, #x2" ).val( "hi" ); + ok( v.form(), "Valid form" ); + $( "#x2" ).val( "not equal" ).blur(); + ok( !v.form(), "Invalid form" ); +}); -test("check(): simple", function() { +test( "check(): simple", function() { expect( 3 ); - var element = $('#firstname')[0]; - var v = $('#testForm1').validate(); - ok( v.size() == 0, 'No errors yet' ); - v.check(element); - ok( v.size() == 1, 'error exists' ); + var element = $( "#firstname" )[ 0 ], + v = $( "#testForm1" ).validate(); + + ok( v.size() === 0, "No errors yet" ); + v.check( element ); + ok( v.size() === 1, "error exists" ); v.errorList = []; - $('#firstname').val("hi"); - v.check(element); - ok( !v.size() == 1, 'No more errors' ); + $( "#firstname" ).val( "hi" ); + v.check( element ); + ok( v.size() === 0, "No more errors" ); }); -test("hide(): input", function() { +test( "hide(): input", function() { expect( 3 ); - var errorLabel = $('#errorFirstname'); - var element = $('#firstname')[0]; - element.value ="bla"; - var v = $('#testForm1').validate(); + var errorLabel = $( "#errorFirstname" ), + element = $( "#firstname" )[ 0 ], + v; + + element.value = "bla"; + v = $( "#testForm1" ).validate(); errorLabel.show(); - ok( errorLabel.is(":visible"), "Error label visible before validation" ); - ok( v.element(element) ); - ok( errorLabel.is(":hidden"), "Error label not visible after validation" ); + + ok( errorLabel.is( ":visible" ), "Error label visible before validation" ); + ok( v.element( element ) ); + ok( errorLabel.is( ":hidden" ), "Error label not visible after validation" ); }); -test("hide(): radio", function() { +test( "hide(): radio", function() { expect( 2 ); - var errorLabel = $('#agreeLabel'); - var element = $('#agb')[0]; + var errorLabel = $( "#agreeLabel" ), + element = $( "#agb" )[ 0 ], + v; + element.checked = true; - var v = $('#testForm2').validate({ errorClass: "xerror" }); + v = $( "#testForm2" ).validate({ errorClass: "xerror" }); errorLabel.show(); - ok( errorLabel.is(":visible"), "Error label visible after validation" ); - v.element(element); - ok( errorLabel.is(":hidden"), "Error label not visible after hiding it" ); + + ok( errorLabel.is( ":visible" ), "Error label visible after validation" ); + v.element( element ); + ok( errorLabel.is( ":hidden" ), "Error label not visible after hiding it" ); }); -test("hide(): errorWrapper", function() { - expect(2); - var errorLabel = $('#errorWrapper'); - var element = $('#meal')[0]; - element.selectedIndex = 1; +test( "hide(): errorWrapper", function() { + expect( 2 ); + var errorLabel = $( "#errorWrapper" ), + element = $( "#meal" )[ 0 ], + v; + element.selectedIndex = 1; errorLabel.show(); - ok( errorLabel.is(":visible"), "Error label visible after validation" ); - var v = $('#testForm3').validate({ wrapper: "li", errorLabelContainer: $("#errorContainer") }); - v.element(element); - ok( errorLabel.is(":hidden"), "Error label not visible after hiding it" ); + + ok( errorLabel.is( ":visible" ), "Error label visible after validation" ); + v = $( "#testForm3" ).validate({ wrapper: "li", errorLabelContainer: $( "#errorContainer" ) }); + v.element( element ); + ok( errorLabel.is( ":hidden" ), "Error label not visible after hiding it" ); }); -test("hide(): container", function() { - expect(4); - var errorLabel = $('#errorContainer'); - var element = $('#testForm3')[0]; - var v = $('#testForm3').validate({ errorWrapper: "li", errorContainer: $("#errorContainer") }); +test( "hide(): container", function() { + expect( 4 ); + var errorLabel = $( "#errorContainer" ), + v = $( "#testForm3" ).validate({ errorWrapper: "li", errorContainer: $( "#errorContainer" ) }); + v.form(); - ok( errorLabel.is(":visible"), "Error label visible after validation" ); - $('#meal')[0].selectedIndex = 1; + ok( errorLabel.is( ":visible" ), "Error label visible after validation" ); + $( "#meal" )[ 0 ].selectedIndex = 1; v.form(); - ok( errorLabel.is(":hidden"), "Error label not visible after hiding it" ); - $('#meal')[0].selectedIndex = -1; - v.element("#meal"); - ok( errorLabel.is(":visible"), "Error label visible after validation" ); - $('#meal')[0].selectedIndex = 1; - v.element("#meal"); - ok( errorLabel.is(":hidden"), "Error label not visible after hiding it" ); -}); - -test("valid()", function() { - expect(4); - var errorList = [{name:"meal",message:"foo", element:$("#meal")[0]}]; - var v = $('#testForm3').validate(); - ok( v.valid(), "No errors, must be valid" ); - v.errorList = errorList; - ok( !v.valid(), "One error, must be invalid" ); - QUnit.reset(); - v = $('#testForm3').validate({ submitHandler: function() { - ok( false, "Submit handler was called" ); - }}); - ok( v.valid(), "No errors, must be valid and returning true, even with the submit handler" ); - v.errorList = errorList; - ok( !v.valid(), "One error, must be invalid, no call to submit handler" ); + ok( errorLabel.is( ":hidden" ), "Error label not visible after hiding it" ); + $( "#meal" )[ 0 ].selectedIndex = -1; + v.element( "#meal" ); + ok( errorLabel.is( ":visible" ), "Error label visible after validation" ); + $( "#meal" )[ 0 ].selectedIndex = 1; + v.element( "#meal" ); + ok( errorLabel.is( ":hidden" ), "Error label not visible after hiding it" ); }); -test("submitHandler keeps submitting button", function() { - $("#userForm").validate({ +test( "submitHandler keeps submitting button", function() { + var button, event; + + $( "#userForm" ).validate({ debug: true, - submitHandler: function(form) { + submitHandler: function( form ) { // dunno how to test this better; this tests the implementation that uses a hidden input - var hidden = $(form).find("input:hidden")[0]; - deepEqual(hidden.value, button.value) - deepEqual(hidden.name, button.name) + var hidden = $( form ).find( "input:hidden" )[ 0 ]; + deepEqual( hidden.value, button.value ); + deepEqual( hidden.name, button.name ); } }); - $("#username").val("bla"); - var button = $("#userForm :submit")[0] - var event = $.Event("click"); - event.preventDefault(); - $.event.trigger(event, null, button); - $("#userForm").submit(); + $( "#username" ).val( "bla" ); + button = $( "#userForm :submit" )[ 0 ]; + event = $.Event( "click" ); + event.preventDefault(); + $.event.trigger( event, null, button ); + $( "#userForm" ).submit(); }); -test("showErrors()", function() { +asyncTest("validation triggered on radio/checkbox when using keyboard", function() { + expect( 1 ); + var input, i, events, triggeredEvents = 0; + + $("#form").validate({ + onfocusin: function() { + triggeredEvents++; + }, + onfocusout: function() { + triggeredEvents++; + }, + onkeyup: function() { + triggeredEvents++; + } + }); + + events = [ + $.Event("focusin"), + $.Event("focusout"), + $.Event("keyup") + ]; + + input = $("#form :radio:first"); + for (i = 0; i < events.length; i++) { + input.trigger(events[i]); + } + + input = $("#form :checkbox:first"); + for (i = 0; i < events.length; i++) { + input.trigger(events[i]); + } + + setTimeout(function() { + // assert all event handlers fired + equal(6, triggeredEvents); + start(); + }); +}); + +asyncTest("validation triggered on radio/checkbox when using mouseclick", function() { + expect( 1 ); + var input, i, events, triggeredEvents = 0; + + $("#form").validate({ + onclick: function() { + triggeredEvents++; + } + }); + + events = [ + $.Event("click") + ]; + + input = $("#form :radio:first"); + for (i = 0; i < events.length; i++) { + input.trigger(events[i]); + } + + input = $("#form :checkbox:first"); + for (i = 0; i < events.length; i++) { + input.trigger(events[i]); + } + + setTimeout(function() { + // assert all event handlers fired + equal(2, triggeredEvents); + start(); + }); +}); + +test( "showErrors()", function() { expect( 4 ); - var errorLabel = $('#errorFirstname').hide(); - var element = $('#firstname')[0]; - var v = $('#testForm1').validate(); - ok( errorLabel.is(":hidden") ); - equal( 0, $("label.error[for=lastname]").size() ); - v.showErrors({"firstname": "required", "lastname": "bla"}); - equal( true, errorLabel.is(":visible") ); - equal( true, $("label.error[for=lastname]").is(":visible") ); -}); - -test("showErrors(), allow empty string and null as default message", function() { - $("#userForm").validate({ + var errorLabel = $( "#errorFirstname" ).hide(), + v = $( "#testForm1" ).validate(); + + ok( errorLabel.is( ":hidden" ) ); + equal( 0, $( "#lastname" ).next( ".error:not(input)" ).length ); + v.showErrors({ "firstname": "required", "lastname": "bla" }); + equal( true, errorLabel.is( ":visible" ) ); + equal( true, $( "#lastname" ).next( ".error:not(input)" ).is( ":visible" ) ); +}); + +test( "showErrors(), allow empty string and null as default message", function() { + $( "#userForm" ).validate({ rules: { username: { required: true, @@ -314,45 +450,48 @@ test("showErrors(), allow empty string and null as default message", function() } } }); - ok( !$("#username").valid() ); - equal( "", $("label.error[for=username]").text() ); + ok( !$( "#username" ).valid() ); + equal( "", $( "#username" ).next( ".error:not(input)" ).text() ); - $("#username").val("ab"); - ok( !$("#username").valid() ); - equal( "too short", $("label.error[for=username]").text() ); + $( "#username" ).val( "ab" ); + ok( !$( "#username" ).valid() ); + equal( "too short", $( "#username" ).next( ".error:not(input)" ).text() ); - $("#username").val("abc"); - ok( $("#username").valid() ); - ok( $("label.error[for=username]").is(":hidden") ); + $( "#username" ).val( "abc" ); + ok( $( "#username" ).valid() ); + ok( $( "#username" ).next( ".error:not(input)" ).is( ":hidden" ) ); }); -test("showErrors() - external messages", function() { +test( "showErrors() - external messages", function() { expect( 4 ); - var methods = $.extend({}, $.validator.methods); - var messages = $.extend({}, $.validator.messages); - $.validator.addMethod("foo", function() { return false; }); - $.validator.addMethod("bar", function() { return false; }); - equal( 0, $("#testForm4 label.error[for=f1]").size() ); - equal( 0, $("#testForm4 label.error[for=f2]").size() ); - var form = $('#testForm4')[0]; - var v = $(form).validate({ + var methods = $.extend( {}, $.validator.methods ), + messages = $.extend( {}, $.validator.messages ), + form, v; + + $.validator.addMethod( "foo", function() { return false; }); + $.validator.addMethod( "bar", function() { return false; }); + equal( 0, $( "#testForm4 #f1" ).next( ".error:not(input)" ).length ); + equal( 0, $( "#testForm4 #f2" ).next( ".error:not(input)" ).length ); + + form = $( "#testForm4" )[ 0 ]; + v = $( form ).validate({ messages: { f1: "Please!", f2: "Wohoo!" } }); v.form(); - equal( $("#testForm4 label.error[for=f1]").text(), "Please!" ); - equal( $("#testForm4 label.error[for=f2]").text(), "Wohoo!" ); + equal( $( "#testForm4 #f1" ).next( ".error:not(input)" ).text(), "Please!" ); + equal( $( "#testForm4 #f2" ).next( ".error:not(input)" ).text(), "Wohoo!" ); $.validator.methods = methods; $.validator.messages = messages; }); -test("showErrors() - custom handler", function() { - expect(5); - var v = $('#testForm1').validate({ - showErrors: function(errorMap, errorList) { +test( "showErrors() - custom handler", function() { + expect( 5 ); + var v = $( "#testForm1" ).validate({ + showErrors: function( errorMap, errorList ) { equal( v, this ); equal( v.errorList, errorList ); equal( v.errorMap, errorMap ); @@ -363,590 +502,622 @@ test("showErrors() - custom handler", function() { v.form(); }); -test("option: (un)highlight, default", function() { - $("#testForm1").validate(); - var e = $("#firstname") - ok( !e.hasClass("error") ); - ok( !e.hasClass("valid") ); - e.valid() - ok( e.hasClass("error") ); - ok( !e.hasClass("valid") ); - e.val("hithere").valid() - ok( !e.hasClass("error") ); - ok( e.hasClass("valid") ); +test( "option: (un)highlight, default", function() { + $( "#testForm1" ).validate(); + var e = $( "#firstname" ); + ok( !e.hasClass( "error" ) ); + ok( !e.hasClass( "valid" ) ); + e.valid(); + ok( e.hasClass( "error" ) ); + ok( !e.hasClass( "valid" ) ); + e.val( "hithere" ).valid(); + ok( !e.hasClass( "error" ) ); + ok( e.hasClass( "valid" ) ); }); -test("option: (un)highlight, nothing", function() { - expect(3); - $("#testForm1").validate({ +test( "option: (un)highlight, nothing", function() { + expect( 3 ); + $( "#testForm1" ).validate({ highlight: false, unhighlight: false }); - var e = $("#firstname") - ok( !e.hasClass("error") ); - e.valid() - ok( !e.hasClass("error") ); - e.valid() - ok( !e.hasClass("error") ); -}); - -test("option: (un)highlight, custom", function() { - expect(5); - $("#testForm1clean").validate({ - highlight: function(element, errorClass) { + var e = $( "#firstname" ); + ok( !e.hasClass( "error" ) ); + e.valid(); + ok( !e.hasClass( "error" ) ); + e.valid(); + ok( !e.hasClass( "error" ) ); +}); + +test( "option: (un)highlight, custom", function() { + expect( 5 ); + $( "#testForm1clean" ).validate({ + highlight: function( element, errorClass ) { equal( "invalid", errorClass ); - $(element).hide(); + $( element ).hide(); }, - unhighlight: function(element, errorClass) { - equal( "invalid", errorClass ) - $(element).show(); + unhighlight: function( element, errorClass ) { + equal( "invalid", errorClass ); + $( element ).show(); }, + ignore: "", errorClass: "invalid", rules: { - firstname: "required" + firstnamec: "required" } }); - var e = $("#firstnamec") - ok( e.is(":visible") ); - e.valid() - ok( !e.is(":visible") ); - e.val("hithere").valid() - ok( e.is(":visible") ); -}); - -test("option: (un)highlight, custom2", function() { - expect(6); - $("#testForm1").validate({ - highlight: function(element, errorClass) { - $(element).addClass(errorClass); - $(element.form).find("label[for=" + element.id + "]").addClass(errorClass); + var e = $( "#firstnamec" ); + ok( e.is( ":visible" ) ); + e.valid(); + ok( !e.is( ":visible" ) ); + e.val( "hithere" ).valid(); + ok( e.is( ":visible" ) ); +}); + +test( "option: (un)highlight, custom2", function() { + expect( 6 ); + var e, l; + $( "#testForm1" ).validate({ + highlight: function( element, errorClass ) { + $( element ).addClass( errorClass ); + $( element ).next( ".error:not(input)" ).addClass( errorClass ); }, - unhighlight: function(element, errorClass) { - $(element).removeClass(errorClass); - $(element.form).find("label[for=" + element.id + "]").removeClass(errorClass); + unhighlight: function( element, errorClass ) { + $( element ).removeClass( errorClass ); + $( element ).next( ".error:not(input)" ).removeClass( errorClass ); }, errorClass: "invalid" }); - var e = $("#firstname") - var l = $("#errorFirstname") - ok( !e.is(".invalid") ); - ok( !l.is(".invalid") ); - e.valid() - ok( e.is(".invalid") ); - ok( l.is(".invalid") ); - e.val("hithere").valid() - ok( !e.is(".invalid") ); - ok( !l.is(".invalid") ); -}); - -test("option: focusCleanup default false", function() { - var form = $("#userForm") + + e = $( "#firstname" ); + l = $( "#errorFirstname" ); + + ok( !e.is( ".invalid" ) ); + ok( !l.is( ".invalid" ) ); + e.valid(); + ok( e.is( ".invalid" ) ); + ok( l.is( ".invalid" ) ); + e.val( "hithere" ).valid(); + ok( !e.is( ".invalid" ) ); + ok( !l.is( ".invalid" ) ); +}); + +test( "option: focusCleanup default false", function() { + var form = $( "#userForm" ); form.validate(); form.valid(); - ok( form.is(":has(label.error[for=username]:visible)")); - $("#username").focus(); - ok( form.is(":has(label.error[for=username]:visible)")); + ok( form.find( "#username" ).next( ".error:not(input)" ).is( ":visible" )); + $( "#username" ).focus(); + ok( form.find( "#username" ).next( ".error:not(input)" ).is( ":visible" )); }); -test("option: focusCleanup true", function() { - var form = $("#userForm") +test( "option: focusCleanup true", function() { + var form = $( "#userForm" ); form.validate({ focusCleanup: true }); form.valid(); - ok( form.is(":has(label.error[for=username]:visible)") ); - $("#username").focus().trigger("focusin"); - ok( !form.is(":has(label.error[for=username]:visible)") ); + ok( form.find( "#username" ).next( ".error:not(input)" ).is( ":visible" ) ); + $( "#username" ).focus().trigger( "focusin" ); + ok( !form.find( "#username" ).next( ".error:not(input)" ).is( ":visible" ) ); }); -test("option: focusCleanup with wrapper", function() { - var form = $("#userForm") +test( "option: focusCleanup with wrapper", function() { + var form = $( "#userForm" ); form.validate({ focusCleanup: true, wrapper: "span" }); form.valid(); - ok( form.is(":has(span:visible:has(label.error[for=username]))") ); - $("#username").focus().trigger("focusin"); - ok( !form.is(":has(span:visible:has(label.error[for=username]))") ); + ok( form.is( ":has(span:visible:has(.error#username-error))" ) ); + $( "#username" ).focus().trigger( "focusin" ); + ok( !form.is( ":has(span:visible:has(.error#username-error))" ) ); }); -test("option: errorClass with multiple classes", function() { - var form = $("#userForm") +test( "option: errorClass with multiple classes", function() { + var form = $( "#userForm" ); form.validate({ focusCleanup: true, wrapper: "span", - errorClass: "error error1" + errorClass: "error error1 error2" }); form.valid(); - ok( form.is(":has(span:visible:has(label.error[for=username]))") ); - ok( form.is(":has(span:visible:has(label.error1[for=username]))") ); - $("#username").focus().trigger("focusin"); - ok( !form.is(":has(span:visible:has(label.error[for=username]))") ); - ok( !form.is(":has(span:visible:has(label.error1[for=username]))") ); -}); - -test("elements() order", function() { - var container = $("#orderContainer"); - var v = $("#elementsOrder").validate({ - errorLabelContainer: container, - wrap: "li" - }); - deepEqual( v.elements().map(function() { - return $(this).attr("id"); - }).get(), ["order1", "order2", "order3", "order4", "order5", "order6"], "elements must be in document order" ); - v.form(); - deepEqual( container.children().map(function() { - return $(this).attr("for"); - }).get(), ["order1", "order2", "order3", "order4", "order5", "order6"], "labels in error container must be in document order" ); + ok( form.is( ":has(span:visible:has(.error#username-error))" ) ); + ok( form.is( ":has(span:visible:has(.error1#username-error))" ) ); + ok( form.is( ":has(span:visible:has(.error2#username-error))" ) ); + $( "#username" ).focus().trigger( "focusin" ); + ok( !form.is( ":has(span:visible:has(.error#username-error))" ) ); + ok( !form.is( ":has(span:visible:has(.error1#username-error))" ) ); + ok( !form.is( ":has(span:visible:has(.error2#username-error))" ) ); }); -test("defaultMessage(), empty title is ignored", function() { - var v = $("#userForm").validate(); - equal( "This field is required.", v.defaultMessage($("#username")[0], "required") ); +test( "defaultMessage(), empty title is ignored", function() { + var v = $( "#userForm" ).validate(); + equal( "This field is required.", v.defaultMessage($( "#username" )[ 0 ], "required" ) ); }); -test("formatAndAdd", function() { - expect(4); - var v = $("#form").validate(); - var fakeElement = { form: $("#form")[0], name: "bar" }; - v.formatAndAdd(fakeElement, {method: "maxlength", parameters: 2}) - equal( "Please enter no more than 2 characters.", v.errorList[0].message ); - equal( "bar", v.errorList[0].element.name ); +test( "formatAndAdd", function() { + expect( 4 ); + var v = $( "#form" ).validate(), + fakeElement = { form: $( "#form" )[ 0 ], name: "bar" }; + + v.formatAndAdd( fakeElement, { method: "maxlength", parameters: 2 }); + equal( "Please enter no more than 2 characters.", v.errorList[ 0 ].message ); + equal( "bar", v.errorList[ 0 ].element.name ); - v.formatAndAdd(fakeElement, {method: "range", parameters:[2,4]}) - equal( "Please enter a value between 2 and 4.", v.errorList[1].message ); + v.formatAndAdd( fakeElement, { method: "range", parameters: [ 2, 4 ] }); + equal( "Please enter a value between 2 and 4.", v.errorList[ 1 ].message ); - v.formatAndAdd(fakeElement, {method: "range", parameters:[0,4]}) - equal( "Please enter a value between 0 and 4.", v.errorList[2].message ); + v.formatAndAdd( fakeElement, { method: "range", parameters: [ 0, 4 ] }); + equal( "Please enter a value between 0 and 4.", v.errorList[ 2 ].message ); }); -test("formatAndAdd2", function() { - expect(3); - var v = $("#form").validate(); - var fakeElement = { form: $("#form")[0], name: "bar" }; - jQuery.validator.messages.test1 = function(param, element) { +test( "formatAndAdd2", function() { + expect( 3 ); + var v = $( "#form" ).validate(), + fakeElement = { form: $( "#form" )[ 0 ], name: "bar" }; + + jQuery.validator.messages.test1 = function( param, element ) { equal( v, this ); equal( 0, param ); return "element " + element.name + " is not valid"; }; - v.formatAndAdd(fakeElement, {method: "test1", parameters: 0}) - equal( "element bar is not valid", v.errorList[0].message ); + v.formatAndAdd( fakeElement, { method: "test1", parameters: 0 }); + equal( "element bar is not valid", v.errorList[ 0 ].message ); }); -test("formatAndAdd, auto detect substitution string", function() { - var v = $("#testForm1clean").validate({ +test( "formatAndAdd, auto detect substitution string", function() { + var v = $( "#testForm1clean" ).validate({ rules: { - firstname: { + firstnamec: { required: true, - rangelength: [5, 10] + rangelength: [ 5, 10 ] } }, messages: { - firstname: { + firstnamec: { rangelength: "at least ${0}, up to {1}" } } }); - $("#firstnamec").val("abc"); + $( "#firstnamec" ).val( "abc" ); v.form(); - equal( "at least 5, up to 10", v.errorList[0].message ); -}) - -test("error containers, simple", function() { - expect(14); - var container = $("#simplecontainer"); - var v = $("#form").validate({ - errorLabelContainer: container, - showErrors: function() { - container.find("h3").html( jQuery.validator.format("There are {0} errors in your form.", this.size()) ); - this.defaultShowErrors(); - } - }); - - v.prepareForm(); - ok( v.valid(), "form is valid" ); - equal( 0, container.find("label").length, "There should be no error labels" ); - equal( "", container.find("h3").html() ); - - v.prepareForm(); - v.errorList = [{message:"bar", element: {name:"foo"}}, {message: "necessary", element: {name:"required"}}]; - ok( !v.valid(), "form is not valid after adding errors manually" ); - v.showErrors(); - equal( container.find("label").length, 2, "There should be two error labels" ); - ok( container.is(":visible"), "Check that the container is visible" ); - container.find("label").each(function() { - ok( $(this).is(":visible"), "Check that each label is visible" ); - }); - equal( "There are 2 errors in your form.", container.find("h3").html() ); - - v.prepareForm(); - ok( v.valid(), "form is valid after a reset" ); - v.showErrors(); - equal( container.find("label").length, 2, "There should still be two error labels" ); - ok( container.is(":hidden"), "Check that the container is hidden" ); - container.find("label").each(function() { - ok( $(this).is(":hidden"), "Check that each label is hidden" ); - }); + equal( "at least 5, up to 10", v.errorList[ 0 ].message ); }); -test("error containers, with labelcontainer I", function() { - expect(16); - var container = $("#container"), - labelcontainer = $("#labelcontainer"); - var v = $("#form").validate({ - errorContainer: container, - errorLabelContainer: labelcontainer, - wrapper: "li" - }); - - ok( v.valid(), "form is valid" ); - equal( 0, container.find("label").length, "There should be no error labels in the container" ); - equal( 0, labelcontainer.find("label").length, "There should be no error labels in the labelcontainer" ); - equal( 0, labelcontainer.find("li").length, "There should be no lis labels in the labelcontainer" ); - - v.errorList = [{message:"bar", element: {name:"foo"}}, {name: "required", message: "necessary", element: {name:"required"}}]; - ok( !v.valid(), "form is not valid after adding errors manually" ); - v.showErrors(); - equal( 0, container.find("label").length, "There should be no error label in the container" ); - equal( 2, labelcontainer.find("label").length, "There should be two error labels in the labelcontainer" ); - equal( 2, labelcontainer.find("li").length, "There should be two error lis in the labelcontainer" ); - ok( container.is(":visible"), "Check that the container is visible" ); - ok( labelcontainer.is(":visible"), "Check that the labelcontainer is visible" ); - var labels = labelcontainer.find("label").each(function() { - ok( $(this).is(":visible"), "Check that each label is visible1" ); - equal( "li", $(this).parent()[0].tagName.toLowerCase(), "Check that each label is wrapped in an li" ); - ok( $(this).parent("li").is(":visible"), "Check that each parent li is visible" ); - }); -}); - -test("errorcontainer, show/hide only on submit", function() { - expect(14); - var container = $("#container"); - var labelContainer = $("#labelcontainer"); - var v = $("#testForm1").bind("invalid-form.validate", function() { - ok( true, "invalid-form event triggered called" ); - }).validate({ - errorContainer: container, - errorLabelContainer: labelContainer, - showErrors: function() { - container.html( jQuery.validator.format("There are {0} errors in your form.", this.numberOfInvalids()) ); - ok( true, "showErrors called" ); - this.defaultShowErrors(); - } - }); - equal( "", container.html(), "must be empty" ); - equal( "", labelContainer.html(), "must be empty" ); - // validate whole form, both showErrors and invalidHandler must be called once - // preferably invalidHandler first, showErrors second - ok( !v.form(), "invalid form" ); - equal( 2, labelContainer.find("label").length ); - equal( "There are 2 errors in your form.", container.html() ); - ok( labelContainer.is(":visible"), "must be visible" ); - ok( container.is(":visible"), "must be visible" ); - - $("#firstname").val("hix").keyup(); - $("#testForm1").triggerHandler("keyup", [jQuery.event.fix({ type: "keyup", target: $("#firstname")[0] })]); - equal( 1, labelContainer.find("label:visible").length ); - equal( "There are 1 errors in your form.", container.html() ); - - $("#lastname").val("abc"); - ok( v.form(), "Form now valid, trigger showErrors but not invalid-form" ); -}); - -test("option invalidHandler", function() { - expect(1); - var v = $("#testForm1clean").validate({ +asyncTest( "option invalidHandler", function() { + expect( 1 ); + $( "#testForm1clean" ).validate({ invalidHandler: function() { ok( true, "invalid-form event triggered called" ); start(); } }); - $("#usernamec").val("asdf").rules("add", { required: true, minlength: 5 }); - stop(); - $("#testForm1clean").submit(); + $( "#usernamec" ).val( "asdf" ).rules( "add", { required: true, minlength: 5 }); + $( "#testForm1clean" ).submit(); }); -test("findByName()", function() { - deepEqual( new $.validator({}, document.getElementById("form")).findByName(document.getElementById("radio1").name).get(), $("#form").find("[name=radio1]").get() ); +test( "findByName()", function() { + deepEqual( + new $.validator({}, document.getElementById( "form" )) + .findByName( document.getElementById( "radio1" ).name ) + .get(), + $( "#form" ).find( "[name=radio1]" ).get() + ); }); -test("focusInvalid()", function() { +test( "focusInvalid()", function() { // TODO when using custom focusin, this is triggered just once // TODO when using 1.4 focusin, triggered twice; fix once not testing against 1.3 anymore - // expect(1); - var inputs = $("#testForm1 input").focus(function() { - equal( inputs[0], this, "focused first element" ); - }); - var v = $("#testForm1").validate(); + // expect( 1 ); + var inputs = $( "#testForm1 input" ).focus(function() { + equal( inputs[ 0 ], this, "focused first element" ); + }), + v = $( "#testForm1" ).validate(); + v.form(); v.focusInvalid(); }); -test("findLastActive()", function() { - expect(3); - var v = $("#testForm1").validate(); +test( "focusInvalid() after validate a custom set of inputs", function() { + var form = $( "#testForm1" ), + validator = form.validate(), + // It's important the order of Valid, Invalid, Valid so last active element it's a valid element before focus + inputs = $( "#firstname, #lastname, #something" ); + + $( "#firstname" ).val( "ok" ); + + ok( !inputs.valid(), "just one invalid"); + + validator.focusInvalid(); + + equal( form[ 0 ].ownerDocument.activeElement, $( "#lastname" )[0], "focused first element" ); +}); + +test( "findLastActive()", function() { + expect( 3 ); + var v = $( "#testForm1" ).validate(), + lastActive; + ok( !v.findLastActive() ); v.form(); v.focusInvalid(); - equal( v.findLastActive(), $("#firstname")[0] ); - var lastActive = $("#lastname").trigger("focus").trigger("focusin")[0]; + equal( v.findLastActive(), $( "#firstname" )[ 0 ] ); + lastActive = $( "#lastname" ).trigger( "focus" ).trigger( "focusin" )[ 0 ]; + equal( v.lastActive, lastActive ); }); -test("validating multiple checkboxes with 'required'", function() { - expect(3); - var checkboxes = $("#form input[name=check3]").attr("checked", false); - equal(checkboxes.size(), 5); - var v = $("#form").validate({ +test("elementValue() finds radios/checkboxes only within the current form", function() { + expect(1); + var v = $("#userForm").validate(), foreignRadio = $("#radio2")[0]; + + ok( !v.elementValue(foreignRadio) ); +}); + +test( "validating multiple checkboxes with 'required'", function() { + expect( 3 ); + var checkboxes = $( "#form input[name=check3]" ).prop( "checked", false ), + v; + equal( checkboxes.length, 5 ); + + v = $( "#form" ).validate({ rules: { check3: "required" } }); v.form(); - equal(v.size(), 1); - checkboxes.filter(":last").attr("checked", true); + + equal( v.size(), 1 ); + checkboxes.filter( ":last" ).prop( "checked", true ); v.form(); - equal(v.size(), 0); + equal( v.size(), 0 ); }); -test("dynamic form", function() { - var counter = 0; +test( "dynamic form", function() { + var counter = 0, + v; function add() { - $("<input class='{required:true}' name='list" + counter++ + "' />").appendTo("#testForm2"); + $( "<input data-rule-required='true' name='list" + counter++ + "' />" ).appendTo( "#testForm2" ); } - function errors(expected, message) { - equal(expected, v.size(), message ); + function errors( expected, message ) { + equal( expected, v.size(), message ); } - var v = $("#testForm2").validate(); + + v = $( "#testForm2" ).validate(); v.form(); - errors(1); + errors( 1 ); add(); v.form(); - errors(2); + errors( 2 ); add(); v.form(); - errors(3); - $("#testForm2 input[name=list1]").remove(); + errors( 3 ); + $( "#testForm2 input[name=list1]" ).remove(); v.form(); - errors(2); + errors( 2 ); add(); v.form(); - errors(3); - $("#testForm2 input[name^=list]").remove(); + errors( 3 ); + $( "#testForm2 input[name^=list]" ).remove(); v.form(); - errors(1); - $("#agb").attr("disabled", true); + errors( 1 ); + $( "#agb" ).attr( "disabled", true ); v.form(); - errors(0); - $("#agb").attr("disabled", false); + errors( 0 ); + $( "#agb" ).attr( "disabled", false ); v.form(); - errors(1); + errors( 1 ); }); -test("idOrName()", function() { - expect(4); - var v = $("#testForm1").validate(); - equal( "form8input", v.idOrName( $("#form8input")[0] ) ); - equal( "check", v.idOrName( $("#form6check1")[0] ) ); - equal( "agree", v.idOrName( $("#agb")[0] ) ); - equal( "button", v.idOrName( $("#form :button")[0] ) ); +test( "idOrName()", function() { + expect( 4 ); + var v = $( "#testForm1" ).validate(); + equal( "form8input", v.idOrName( $( "#form8input" )[ 0 ] ) ); + equal( "check", v.idOrName( $( "#form6check1" )[ 0 ] ) ); + equal( "agree", v.idOrName( $( "#agb" )[ 0 ] ) ); + equal( "button", v.idOrName( $( "#form :button" )[ 0 ] ) ); }); -test("resetForm()", function() { - function errors(expected, message) { - equal(expected, v.size(), message ); +test( "resetForm()", function() { + function errors( expected, message ) { + equal( expected, v.size(), message ); } - var v = $("#testForm1").validate(); + var v = $( "#testForm1" ).validate(); v.form(); - errors(2); - $("#firstname").val("hiy"); + errors( 2 ); + ok( $( "#firstname" ).hasClass( "error" ) ); + $( "#firstname" ).val( "hiy" ); v.resetForm(); - errors(0); - equal("", $("#firstname").val(), "form plugin is included, therefor resetForm must also reset inputs, not only errors"); + errors( 0 ); + ok( !$( "#firstname" ).hasClass( "error" ) ); + equal( "", $( "#firstname" ).val(), "form plugin is included, therefor resetForm must also reset inputs, not only errors" ); }); -test("message from title", function() { - var v = $("#withTitle").validate(); - v.checkForm(); - equal(v.errorList[0].message, "fromtitle", "title not used"); +test( "resetForm() clean styles when custom highlight function is used", function() { + var form = $( "#testForm1clean" ), + e = $( "#firstnamec" ); + form.validate({ + highlight: function( element ) { + $( element ).hide(); + }, + unhighlight: function( element ) { + $( element ).show(); + }, + ignore: "", + errorClass: "invalid", + rules: { + firstnamec: "required" + } + }); + e.valid(); + ok( !e.is( ":visible" ) ); + form.validate().resetForm(); + ok( e.is( ":visible" ) ); }); -test("ignoreTitle", function() { - var v = $("#withTitle").validate({ignoreTitle:true}); - v.checkForm(); - equal(v.errorList[0].message, $.validator.messages["required"], "title used when it should have been ignored"); +test( "message from title", function() { + var v = $( "#withTitle" ).validate(); + v.checkForm(); + equal( v.errorList[ 0 ].message, "fromtitle", "title not used" ); }); -test("ajaxSubmit", function() { - expect(1); - stop(); - $("#user").val("Peter"); - $("#password").val("foobar"); - jQuery("#signupForm").validate({ - submitHandler: function(form) { - jQuery(form).ajaxSubmit({ - success: function(response) { - equal("Hi Peter, welcome back.", response); +test( "ignoreTitle", function() { + var v = $( "#withTitle" ).validate({ ignoreTitle: true }); + v.checkForm(); + equal( v.errorList[ 0 ].message, $.validator.messages.required, "title used when it should have been ignored" ); +}); + +asyncTest( "ajaxSubmit", function() { + expect( 1 ); + $( "#user" ).val( "Peter" ); + $( "#password" ).val( "foobar" ); + jQuery( "#signupForm" ).validate({ + submitHandler: function( form ) { + jQuery( form ).ajaxSubmit({ + success: function( response ) { + equal( "Hi Peter, welcome back.", response ); start(); } }); } }); - jQuery("#signupForm").triggerHandler("submit"); + jQuery( "#signupForm" ).triggerHandler( "submit" ); }); - -module("misc"); - -test("success option", function() { - expect(7); - equal( "", $("#firstname").val() ); - var v = $("#testForm1").validate({ - success: "valid" - }); - var label = $("#testForm1 label"); - ok( label.is(".error") ); - ok( !label.is(".valid") ); +test( "validating groups settings parameter", function() { + var form = $( "<form>" ), + validate = form.validate({ + groups: { + arrayGroup: [ "input one", "input-two", "input three" ], + stringGroup: "input-four input-five input-six" + } + }); + + equal( validate.groups[ "input one" ], "arrayGroup" ); + equal( validate.groups[ "input-two" ], "arrayGroup" ); + equal( validate.groups[ "input three" ], "arrayGroup" ); + equal( validate.groups[ "input-four" ], "stringGroup" ); + equal( validate.groups[ "input-five" ], "stringGroup" ); + equal( validate.groups[ "input-six" ], "stringGroup" ); +}); + +test( "bypassing validation on form submission", function() { + var form = $( "#bypassValidation" ), + normalSubmission = $( "form#bypassValidation :input[id=normalSubmit]" ), + bypassSubmitWithCancel = $( "form#bypassValidation :input[id=bypassSubmitWithCancel]" ), + bypassSubmitWithNoValidate1 = $( "form#bypassValidation :input[id=bypassSubmitWithNoValidate1]" ), + bypassSubmitWithNoValidate2 = $( "form#bypassValidation :input[id=bypassSubmitWithNoValidate2]" ), + $v = form.validate({ + debug: true + }); + + bypassSubmitWithCancel.click(); + equal($v.numberOfInvalids(), 0, "Validation was bypassed using CSS 'cancel' class." ); + $v.resetForm(); + + bypassSubmitWithNoValidate1.click(); + equal($v.numberOfInvalids(), 0, "Validation was bypassed using blank 'formnovalidate' attribute." ); + $v.resetForm(); + + bypassSubmitWithNoValidate2.click(); + equal($v.numberOfInvalids(), 0, "Validation was bypassed using 'formnovalidate=\"formnovalidate\"' attribute." ); + $v.resetForm(); + + normalSubmission.click(); + equal($v.numberOfInvalids(), 1, "Validation failed correctly" ); +}); + +module( "misc" ); + +test( "success option", function() { + expect( 7 ); + equal( "", $( "#firstname" ).val() ); + var v = $( "#testForm1" ).validate({ + success: "valid" + }), + label = $( "#testForm1 .error:not(input)" ); + + ok( label.is( ".error" ) ); + ok( !label.is( ".valid" ) ); v.form(); - ok( label.is(".error") ); - ok( !label.is(".valid") ); - $("#firstname").val("hi"); + ok( label.is( ".error" ) ); + ok( !label.is( ".valid" ) ); + $( "#firstname" ).val( "hi" ); v.form(); - ok( label.is(".error") ); - ok( label.is(".valid") ); + ok( label.is( ".error" ) ); + ok( label.is( ".valid" ) ); }); -test("success option2", function() { - expect(5); - equal( "", $("#firstname").val() ); - var v = $("#testForm1").validate({ - success: "valid" - }); - var label = $("#testForm1 label"); - ok( label.is(".error") ); - ok( !label.is(".valid") ); - $("#firstname").val("hi"); +test( "success option2", function() { + expect( 5 ); + equal( "", $( "#firstname" ).val() ); + var v = $( "#testForm1" ).validate({ + success: "valid" + }), + label = $( "#testForm1 .error:not(input)" ); + + ok( label.is( ".error" ) ); + ok( !label.is( ".valid" ) ); + $( "#firstname" ).val( "hi" ); v.form(); - ok( label.is(".error") ); - ok( label.is(".valid") ); + ok( label.is( ".error" ) ); + ok( label.is( ".valid" ) ); }); -test("success option3", function() { - expect(5); - equal( "", $("#firstname").val() ); - $("#errorFirstname").remove(); - var v = $("#testForm1").validate({ - success: "valid" - }); - equal( 0, $("#testForm1 label").size() ); - $("#firstname").val("hi"); +test( "success option3", function() { + expect( 5 ); + equal( "", $( "#firstname" ).val() ); + $( "#errorFirstname" ).remove(); + var v = $( "#testForm1" ).validate({ + success: "valid" + }), + labels; + + equal( 0, $( "#testForm1 .error:not(input)" ).length ); + $( "#firstname" ).val( "hi" ); v.form(); - var labels = $("#testForm1 label"); - equal( 3, labels.size() ); - ok( labels.eq(0).is(".valid") ); - ok( !labels.eq(1).is(".valid") ); + labels = $( "#testForm1 .error:not(input)" ); + + equal( 3, labels.length ); + ok( labels.eq( 0 ).is( ".valid" ) ); + ok( !labels.eq( 1 ).is( ".valid" ) ); }); -test("successlist", function() { - var v = $("#form").validate({ success: "xyz" }); +test( "successlist", function() { + var v = $( "#form" ).validate({ success: "xyz" }); v.form(); - equal(0, v.successList.length); + equal( 0, v.successList.length ); }); -test("success isn't called for optional elements", function() { - expect(4); - equal( "", $("#firstname").removeClass().val() ); - $("#something").remove(); - $("#lastname").remove(); - $("#errorFirstname").remove(); - var v = $("#testForm1").validate({ +test( "success isn't called for optional elements with no other rules", function() { + expect( 4 ); + equal( "", $( "#firstname" ).removeAttr( "data-rule-required" ).removeAttr( "data-rule-minlength" ).val() ); + $( "#something" ).remove(); + $( "#lastname" ).remove(); + $( "#errorFirstname" ).remove(); + var v = $( "#testForm1" ).validate({ success: function() { ok( false, "don't call success for optional elements!" ); }, rules: { - firstname: "email" + firstname: { required: false } } }); - equal( 0, $("#testForm1 label").size() ); + equal( 0, $( "#testForm1 .error:not(input)" ).length ); v.form(); - equal( 0, $("#testForm1 label").size() ); - $("#firstname").valid(); - equal( 0, $("#testForm1 label").size() ); + equal( 0, $( "#testForm1 .error:not(input)" ).length ); + $( "#firstname" ).valid(); + equal( 0, $( "#testForm1 .error:not(input)" ).length ); }); -test("success callback with element", function() { - expect(1); - var v = $("#userForm").validate({ +test( "success is called for optional elements with other rules", function() { + expect( 1 ); + + $.validator.addMethod( "custom1", function() { + return true; + }, "" ); + + $( "#testForm1clean" ).validate({ + success: function() { + ok( true, "success called correctly!" ); + }, + rules: { + firstnamec: { + required: false, + custom1: true + } + } + }); + + $( "#firstnamec" ).valid(); + + delete $.validator.methods.custom1; +}); + +test( "success callback with element", function() { + expect( 1 ); + var v = $( "#userForm" ).validate({ success: function( label, element ) { - equal( element, $('#username').get(0) ); + equal( element, $( "#username" ).get( 0 ) ); } }); - $("#username").val("hi"); + $( "#username" ).val( "hi" ); v.form(); }); -test("all rules are evaluated even if one returns a dependency-mistmatch", function() { - expect(6); - equal( "", $("#firstname").removeClass().val() ); - $("#lastname").remove(); - $("#errorFirstname").remove(); - $.validator.addMethod("custom1", function() { +test( "all rules are evaluated even if one returns a dependency-mistmatch", function() { + expect( 6 ); + equal( "", $( "#firstname" ).removeAttr( "data-rule-required" ).removeAttr( "data-rule-minlength" ).val() ); + $( "#lastname" ).remove(); + $( "#errorFirstname" ).remove(); + $.validator.addMethod( "custom1", function() { ok( true, "custom method must be evaluated" ); return true; - }, ""); - var v = $("#testForm1").validate({ + }, "" ); + var v = $( "#testForm1" ).validate({ rules: { - firstname: {email:true, custom1: true} + firstname: { + email: true, + custom1: true + } } }); - equal( 0, $("#testForm1 label").size() ); + equal( 0, $( "#testForm1 .error:not(input)" ).length ); v.form(); - equal( 0, $("#testForm1 label").size() ); - $("#firstname").valid(); - equal( 0, $("#testForm1 label").size() ); + equal( 0, $( "#testForm1 .error:not(input)" ).length ); + $( "#firstname" ).valid(); + equal( 0, $( "#testForm1 .error:not(input)" ).length ); delete $.validator.methods.custom1; delete $.validator.messages.custom1; }); -test("messages", function() { +test( "messages", function() { var m = jQuery.validator.messages; - equal( "Please enter no more than 0 characters.", m.maxlength(0) ); - equal( "Please enter at least 1 characters.", m.minlength(1) ); - equal( "Please enter a value between 1 and 2 characters long.", m.rangelength([1, 2]) ); - equal( "Please enter a value less than or equal to 1.", m.max(1) ); - equal( "Please enter a value greater than or equal to 0.", m.min(0) ); - equal( "Please enter a value between 1 and 2.", m.range([1, 2]) ); -}); - -test("jQuery.validator.format", function() { - equal( "Please enter a value between 0 and 1.", jQuery.validator.format("Please enter a value between {0} and {1}.", 0, 1) ); - equal( "0 is too fast! Enter a value smaller then 0 and at least -15", jQuery.validator.format("{0} is too fast! Enter a value smaller then {0} and at least {1}", 0, -15) ); - var template = jQuery.validator.format("{0} is too fast! Enter a value smaller then {0} and at least {1}"); - equal( "0 is too fast! Enter a value smaller then 0 and at least -15", template(0, -15) ); - template = jQuery.validator.format("Please enter a value between {0} and {1}."); - equal( "Please enter a value between 1 and 2.", template([1, 2]) ); -}); - -test("option: ignore", function() { - var v = $("#testForm1").validate({ + equal( "Please enter no more than 0 characters.", m.maxlength( 0 ) ); + equal( "Please enter at least 1 characters.", m.minlength( 1 ) ); + equal( "Please enter a value between 1 and 2 characters long.", m.rangelength( [ 1, 2 ] ) ); + equal( "Please enter a value less than or equal to 1.", m.max( 1 ) ); + equal( "Please enter a value greater than or equal to 0.", m.min( 0 ) ); + equal( "Please enter a value between 1 and 2.", m.range( [ 1, 2 ] ) ); +}); + +test( "jQuery.validator.format", function() { + equal( + "Please enter a value between 0 and 1.", + jQuery.validator.format( "Please enter a value between {0} and {1}.", 0, 1 ) + ); + equal( + "0 is too fast! Enter a value smaller then 0 and at least -15", + jQuery.validator.format( "{0} is too fast! Enter a value smaller then {0} and at least {1}", 0, -15 ) + ); + var template = jQuery.validator.format( "{0} is too fast! Enter a value smaller then {0} and at least {1}" ); + equal( "0 is too fast! Enter a value smaller then 0 and at least -15", template( 0, -15 ) ); + template = jQuery.validator.format( "Please enter a value between {0} and {1}." ); + equal( "Please enter a value between 1 and 2.", template( [ 1, 2 ] ) ); + equal( $.validator.format( "{0}", "$0" ), "$0" ); +}); + +test( "option: ignore", function() { + var v = $( "#testForm1" ).validate({ ignore: "[name=lastname]" }); v.form(); equal( 1, v.size() ); }); -test("option: subformRequired", function() { - jQuery.validator.addMethod("billingRequired", function(value, element) { - if ($("#bill_to_co").is(":checked")) - return $(element).parents("#subform").length; - return !this.optional(element); - }, ""); - var v = $("#subformRequired").validate(); +test( "option: subformRequired", function() { + jQuery.validator.addMethod( "billingRequired", function( value, element ) { + if ($( "#bill_to_co" ).is( ":checked" )) { + return $( element ).parents( "#subform" ).length; + } + return !this.optional( element ); + }, "" ); + var v = $( "#subformRequired" ).validate(); v.form(); equal( 1, v.size() ); - $("#bill_to_co").attr("checked", false); + $( "#bill_to_co" ).attr( "checked", false ); v.form(); equal( 2, v.size() ); @@ -954,198 +1125,285 @@ test("option: subformRequired", function() { delete $.validator.messages.billingRequired; }); -module("expressions"); +module( "expressions" ); -test("expression: :blank", function() { - var e = $("#lastname")[0]; - equal( 1, $(e).filter(":blank").length ); +test( "expression: :blank", function() { + var e = $( "#lastname" )[ 0 ]; + equal( 1, $( e ).filter( ":blank" ).length ); e.value = " "; - equal( 1, $(e).filter(":blank").length ); - e.value = " " - equal( 1, $(e).filter(":blank").length ); - e.value= " a "; - equal( 0, $(e).filter(":blank").length ); + equal( 1, $( e ).filter( ":blank" ).length ); + e.value = " "; + equal( 1, $( e ).filter( ":blank" ).length ); + e.value = " a "; + equal( 0, $( e ).filter( ":blank" ).length ); }); -test("expression: :filled", function() { - var e = $("#lastname")[0]; - equal( 0, $(e).filter(":filled").length ); +test( "expression: :filled", function() { + var e = $( "#lastname" )[ 0 ]; + equal( 0, $( e ).filter( ":filled" ).length ); e.value = " "; - equal( 0, $(e).filter(":filled").length ); - e.value = " " - equal( 0, $(e).filter(":filled").length ); - e.value= " a "; - equal( 1, $(e).filter(":filled").length ); + equal( 0, $( e ).filter( ":filled" ).length ); + e.value = " "; + equal( 0, $( e ).filter( ":filled" ).length ); + e.value = " a "; + equal( 1, $( e ).filter( ":filled" ).length ); }); -test("expression: :unchecked", function() { - var e = $("#check2")[0]; - equal( 1, $(e).filter(":unchecked").length ); +test( "expression: :unchecked", function() { + var e = $( "#check2" )[ 0 ]; + equal( 1, $( e ).filter( ":unchecked" ).length ); e.checked = true; - equal( 0, $(e).filter(":unchecked").length ); + equal( 0, $( e ).filter( ":unchecked" ).length ); e.checked = false; - equal( 1, $(e).filter(":unchecked").length ); + equal( 1, $( e ).filter( ":unchecked" ).length ); }); -module("events"); +module( "events" ); -test("validate on blur", function() { - function errors(expected, message) { - equal(v.size(), expected, message ); +test( "validate on blur", function() { + function errors( expected, message ) { + equal( v.size(), expected, message ); } - function labels(expected) { - equal(v.errors().filter(":visible").size(), expected); + function labels( expected ) { + equal( v.errors().filter( ":visible" ).length, expected ); } - function blur(target) { - target.trigger("blur").trigger("focusout"); + function blur( target ) { + target.trigger( "blur" ).trigger( "focusout" ); } - $("#errorFirstname").hide(); - var e = $("#firstname"); - var v = $("#testForm1").validate(); - $("#something").val(""); - blur(e); - errors(0, "No value yet, required is skipped on blur"); - labels(0); - e.val("h"); - blur(e); - errors(1, "Required was ignored, but as something was entered, check other rules, minlength isn't met"); - labels(1); - e.val("hh"); - blur(e); - errors(0, "All is fine"); - labels(0); - e.val(""); + $( "#errorFirstname" ).hide(); + var e = $( "#firstname" ), + v = $( "#testForm1" ).validate(); + + $( "#something" ).val( "" ); + blur( e ); + errors( 0, "No value yet, required is skipped on blur" ); + labels( 0 ); + e.val( "h" ); + blur( e ); + errors( 1, "Required was ignored, but as something was entered, check other rules, minlength isn't met" ); + labels( 1 ); + e.val( "hh" ); + blur( e ); + errors( 0, "All is fine" ); + labels( 0 ); + e.val( "" ); v.form(); - errors(3, "Submit checks all rules, both fields invalid"); - labels(3); - blur(e); - errors(1, "Blurring the field results in emptying the error list first, then checking the invalid field: its still invalid, don't remove the error" ); - labels(3); - e.val("h"); - blur(e); - errors(1, "Entering a single character fulfills required, but not minlength: 2, still invalid"); - labels(3); - e.val("hh"); - blur(e); - errors(0, "Both required and minlength are met, no errors left"); - labels(2); -}); - -test("validate on keyup", function() { - function errors(expected, message) { - equal(expected, v.size(), message ); + errors( 3, "Submit checks all rules, both fields invalid" ); + labels( 3 ); + blur( e ); + errors( 1, "Blurring the field results in emptying the error list first, then checking the invalid field: its still invalid, don't remove the error" ); + labels( 3 ); + e.val( "h" ); + blur( e ); + errors( 1, "Entering a single character fulfills required, but not minlength: 2, still invalid" ); + labels( 3 ); + e.val( "hh" ); + blur( e ); + errors( 0, "Both required and minlength are met, no errors left" ); + labels( 2 ); +}); + +test( "validate on keyup", function() { + function errors( expected, message ) { + equal( expected, v.size(), message ); } - function keyup(target) { - target.trigger("keyup"); + function keyup( target ) { + target.trigger( "keyup" ); } - var e = $("#firstname"); - var v = $("#testForm1").validate(); - keyup(e); - errors(0, "No value, no errors"); - e.val("a"); - keyup(e); - errors(0, "Value, but not invalid"); - e.val(""); + var e = $( "#firstname" ), + v = $( "#testForm1" ).validate(); + + keyup( e ); + errors( 0, "No value, no errors" ); + e.val( "a" ); + keyup( e ); + errors( 0, "Value, but not invalid" ); + e.val( "" ); v.form(); - errors(2, "Both invalid"); - keyup(e); - errors(1, "Only one field validated, still invalid"); - e.val("hh"); - keyup(e); - errors(0, "Not invalid anymore"); - e.val("h"); - keyup(e); - errors(1, "Field didn't loose focus, so validate again, invalid"); - e.val("hh"); - keyup(e); - errors(0, "Valid"); -}); - -test("validate on not keyup, only blur", function() { - function errors(expected, message) { - equal(expected, v.size(), message ); + errors( 2, "Both invalid" ); + keyup( e ); + errors( 1, "Only one field validated, still invalid" ); + e.val( "hh" ); + keyup( e ); + errors( 0, "Not invalid anymore" ); + e.val( "h" ); + keyup( e ); + errors( 1, "Field didn't loose focus, so validate again, invalid" ); + e.val( "hh" ); + keyup( e ); + errors( 0, "Valid" ); +}); + +test( "validate on not keyup, only blur", function() { + function errors( expected, message ) { + equal( expected, v.size(), message ); } - var e = $("#firstname"); - var v = $("#testForm1").validate({ - onkeyup: false - }); - errors(0); - e.val("a"); - e.trigger("keyup"); + var e = $( "#firstname" ), + v = $( "#testForm1" ).validate({ + onkeyup: false + }); + + errors( 0 ); + e.val( "a" ); + e.trigger( "keyup" ); e.keyup(); - errors(0); - e.trigger("blur").trigger("focusout"); - errors(1); + errors( 0 ); + e.trigger( "blur" ).trigger( "focusout" ); + errors( 1 ); }); -test("validate on keyup and blur", function() { - function errors(expected, message) { - equal(expected, v.size(), message ); +test( "validate on keyup and blur", function() { + function errors( expected, message ) { + equal( expected, v.size(), message ); } - var e = $("#firstname"); - var v = $("#testForm1").validate(); - errors(0); - e.val("a"); - e.trigger("keyup"); - errors(0); - e.trigger("blur").trigger("focusout"); - errors(1); -}); - -test("validate email on keyup and blur", function() { - function errors(expected, message) { - equal(expected, v.size(), message ); + var e = $( "#firstname" ), + v = $( "#testForm1" ).validate(); + + errors( 0 ); + e.val( "a" ); + e.trigger( "keyup" ); + errors( 0 ); + e.trigger( "blur" ).trigger( "focusout" ); + errors( 1 ); +}); + +test( "validate email on keyup and blur", function() { + function errors( expected, message ) { + equal( expected, v.size(), message ); } - var e = $("#firstname"); - var v = $("#testForm1").validate(); + var e = $( "#firstname" ), + v = $( "#testForm1" ).validate(); + v.form(); - errors(2); - e.val("a"); - e.trigger("keyup"); - errors(1); - e.val("aa"); - e.trigger("keyup"); - errors(0); -}); - -test("validate checkbox on click", function() { - function errors(expected, message) { - equal(expected, v.size(), message ); + errors( 2 ); + e.val( "a" ); + e.trigger( "keyup" ); + errors( 1 ); + e.val( "aa" ); + e.trigger( "keyup" ); + errors( 0 ); +}); + +test( "don't revalidate the field when pressing special characters", function() { + function errors( expected, message ) { + equal( expected, v.size(), message ); + } + + function triggerEvent( element, keycode ) { + var event = $.Event( "keyup", { keyCode: keycode } ); + element.trigger( event ); } - function trigger(element) { + + var e = $( "#firstname" ), + v = $( "#testForm1" ).validate(), + excludedKeys = { + "Shift": 16, + "Ctrl": 17, + "Alt": 18, + "Caps lock": 20, + "End": 35, + "Home": 36, + "Left arrow": 37, + "Up arrow": 38, + "Right arrow": 39, + "Down arrow": 40, + "Insert": 45, + "Num lock": 144, + "Alt GR": 225 + }; + + // To make sure there is only one error, that one of #firtname field + $( "#firstname" ).val( "" ); + $( "#lastname" ).val( "something" ); + $( "#something" ).val( "something" ); + + // Validate the form + v.form(); + errors( 1, "Validate manualy" ); + + // Check for special keys + e.val( "aaa" ); + $.each( excludedKeys, function( key, keyCode ) { + triggerEvent( e, keyCode ); + errors( 1, key + " key" ); + }); + + // Normal keyup + e.val( "aaaaa" ); + e.trigger( "keyup" ); + errors( 0, "Normal keyup" ); +}); + +test( "validate checkbox on click", function() { + function errors( expected, message ) { + equal( expected, v.size(), message ); + } + function trigger( element ) { element.click(); // triggered click event screws up checked-state in 1.4 element.valid(); } - var e = $("#check2"); - var v = $("#form").validate({ - rules: { - check2: "required" - } - }); - trigger(e); - errors(0); - trigger(e); + var e = $( "#check2" ), + v = $( "#form" ).validate({ + rules: { + check2: "required" + } + }); + + trigger( e ); + errors( 0 ); + trigger( e ); equal( false, v.form() ); - errors(1); - trigger(e); - errors(0); - trigger(e); - errors(1); + errors( 1 ); + trigger( e ); + errors( 0 ); + trigger( e ); + errors( 1 ); }); -test("validate multiple checkbox on click", function() { - function errors(expected, message) { - equal(expected, v.size(), message ); +test( "validate multiple checkbox on click", function() { + function errors( expected, message ) { + equal( expected, v.size(), message ); } - function trigger(element) { + function trigger( element ) { element.click(); // triggered click event screws up checked-state in 1.4 element.valid(); } - var e1 = $("#check1").attr("checked", false); - var e2 = $("#check1b"); - var v = $("#form").validate({ + var e1 = $( "#check1" ).attr( "checked", false ), + e2 = $( "#check1b" ), + v = $( "#form" ).validate({ + rules: { + check: { + required: true, + minlength: 2 + } + } + }); + + trigger( e1 ); + trigger( e2 ); + errors( 0 ); + trigger( e2 ); + equal( false, v.form() ); + errors( 1 ); + trigger( e2 ); + errors( 0 ); + trigger( e2 ); + errors( 1 ); +}); + +test( "correct checkbox receives the error", function() { + function trigger( element ) { + element.click(); + // triggered click event screws up checked-state in 1.4 + element.valid(); + } + var e1 = $( "#check1" ).attr( "checked", false ), + v; + + $( "#check1b" ).attr( "checked", false ); + v = $( "#form" ).find( "[type=checkbox]" ).attr( "checked", false ).end().validate({ rules: { check: { required: true, @@ -1153,114 +1411,443 @@ test("validate multiple checkbox on click", function() { } } }); - trigger(e1); - trigger(e2); - errors(0); - trigger(e2); - equal( false, v.form() ); - errors(1); - trigger(e2); - errors(0); - trigger(e2); - errors(1); + + equal( false, v.form()); + trigger( e1 ); + equal( false, v.form()); + ok( v.errorList[ 0 ].element.id === v.currentElements[ 0 ].id, "the proper checkbox has the error AND is present in currentElements" ); }); -test("correct checkbox receives the error", function(){ - function trigger(element) { - element.click(); - // triggered click event screws up checked-state in 1.4 - element.valid(); - } - var e1 = $("#check1").attr("checked", false); - var e2 = $("#check1b").attr("checked", false); - var v = $("#form").find('[type=checkbox]').attr('checked', false).end().validate({ - rules:{ - check: { - required: true, - minlength: 2 - } - } - }); - equal(false, v.form()); - trigger(e1); - equal(false, v.form()); - ok(v.errorList[0].element.id === v.currentElements[0].id, "the proper checkbox has the error AND is present in currentElements"); -}); - -test("validate radio on click", function() { - function errors(expected, message) { - equal(expected, v.size(), message ); +test( "validate radio on click", function() { + function errors( expected, message ) { + equal( expected, v.size(), message ); } - function trigger(element) { + function trigger( element ) { element.click(); // triggered click event screws up checked-state in 1.4 element.valid(); } - var e1 = $("#radio1"); - var e2 = $("#radio1a"); - var v = $("#form").validate({ - rules: { - radio1: "required" - } - }); - errors(0); + var e1 = $( "#radio1" ), + e2 = $( "#radio1a" ), + v = $( "#form" ).validate({ + rules: { + radio1: "required" + } + }); + + errors( 0 ); equal( false, v.form() ); - errors(1); - trigger(e2); - errors(0); - trigger(e1); - errors(0); + errors( 1 ); + trigger( e2 ); + errors( 0 ); + trigger( e1 ); + errors( 0 ); }); -test("validate input with no type attribute, defaulting to text", function() { - function errors(expected, message) { - equal(expected, v.size(), message ); +test( "validate input with no type attribute, defaulting to text", function() { + function errors( expected, message ) { + equal( expected, v.size(), message ); } - var v = $("#testForm12").validate(); - var e = $("#testForm12text"); - errors(0); + var v = $( "#testForm12" ).validate(), + e = $( "#testForm12text" ); + + errors( 0 ); e.valid(); - errors(1); - e.val('test'); - e.trigger('keyup'); - errors(0); -}); - -test("ignore hidden elements", function(){ - var form = $('#userForm'); - var validate = form.validate({ - rules:{ - "username": "required" - } - }); - form.get(0).reset(); - ok(! validate.form(), "form should be initially invalid"); - $('#userForm [name=username]').hide(); - ok(validate.form(), "hidden elements should be ignored by default"); -}); - -test("ignore hidden elements at start", function(){ - var form = $('#userForm'); - var validate = form.validate({ - rules:{ - "username": "required" - } - }); - form.get(0).reset(); - $('#userForm [name=username]').hide(); - ok(validate.form(), "hidden elements should be ignored by default"); - $('#userForm [name=username]').show(); - ok(! validate.form(), "form should be invalid when required element is visible"); -}); - -test("Specify error messages through data attributes", function() { - var form = $('#dataMessages'); - var name = $('#dataMessagesName'); - var v = form.validate(); - - form.get(0).reset(); + errors( 1 ); + e.val( "test" ); + e.trigger( "keyup" ); + errors( 0 ); +}); + +module( "ignore hidden" ); + +test( "ignore hidden elements", function() { + var form = $( "#userForm" ), + validate = form.validate({ + rules: { + "username": "required" + } + }); + + form.get( 0 ).reset(); + ok( !validate.form(), "form should be initially invalid" ); + $( "#userForm [name=username]" ).hide(); + ok( validate.form(), "hidden elements should be ignored by default" ); +}); + +test( "ignore hidden elements at start", function() { + var form = $( "#userForm" ), + validate = form.validate({ + rules: { + "username": "required" + } + }); + + form.get( 0 ).reset(); + $( "#userForm [name=username]" ).hide(); + ok( validate.form(), "hidden elements should be ignored by default" ); + $( "#userForm [name=username]" ).show(); + ok( !validate.form(), "form should be invalid when required element is visible" ); +}); + +module( "configuration with attributes " ); + +test( "Specify error messages through data attributes", function() { + var form = $( "#dataMessages" ), + name = $( "#dataMessagesName" ), + label; + + form.validate(); + + form.get( 0 ).reset(); name.valid(); - var label = $('#dataMessages label'); + label = $( "#dataMessages .error:not(input)" ); equal( label.text(), "You must enter a value here", "Correct error label" ); }); + +test( "Updates pre-existing label if has error class", function() { + var form = $( "#updateLabel" ), + input = $( "#updateLabelInput" ), + label = $( "#targetLabel" ), + labelsBefore = form.find( ".error:not(input)" ).length, + labelsAfter; + + form.validate(); + input.val( "" ); + input.valid(); + labelsAfter = form.find( ".error:not(input)" ).length; + + // label was updated + equal( label.text(), input.attr( "data-msg-required" ) ); + // new label wasn't created + equal( labelsBefore, labelsAfter ); +}); + +test( "Min date set by attribute", function() { + var form = $( "#rangesMinDateInvalid" ), + name = $( "#minDateInvalid" ), + label; + + form.validate(); + form.get( 0 ).reset(); + name.valid(); + + label = $( "#rangesMinDateInvalid .error:not(input)" ); + equal( label.text(), "Please enter a value greater than or equal to 2012-12-21.", "Correct error label" ); +}); + +test( "Max date set by attribute", function() { + var form = $( "#ranges" ), + name = $( "#maxDateInvalid" ), + label; + + form.validate(); + form.get( 0 ).reset(); + name.valid(); + + label = $( "#ranges .error:not(input)" ); + equal( label.text(), "Please enter a value less than or equal to 2012-12-21.", "Correct error label" ); +}); + +test( "Min and Max date set by attributes greater", function() { + var form = $( "#ranges" ), + name = $( "#rangeDateInvalidGreater" ), + label; + + form.validate(); + form.get( 0 ).reset(); + name.valid(); + + label = $( "#ranges .error:not(input)" ); + equal( label.text(), "Please enter a value less than or equal to 2013-01-21.", "Correct error label" ); +}); + +test( "Min and Max date set by attributes less", function() { + var form = $( "#ranges" ), + name = $( "#rangeDateInvalidLess" ), + label; + + form.validate(); + form.get( 0 ).reset(); + name.valid(); + + label = $( "#ranges .error:not(input)" ); + equal( label.text(), "Please enter a value greater than or equal to 2012-11-21.", "Correct error label" ); +}); + +test( "Min date set by attribute valid", function() { + var form = $( "#rangeMinDateValid" ), + name = $( "#minDateValid" ), + label; + + form.validate(); + form.get( 0 ).reset(); + name.valid(); + + label = $( "#rangeMinDateValid .error:not(input)" ); + equal( label.text(), "", "Correct error label" ); +}); + +test( "Max date set by attribute valid", function() { + var form = $( "#ranges" ), + name = $( "#maxDateValid" ), + label; + + form.validate(); + form.get( 0 ).reset(); + name.valid(); + + label = $( "#ranges .error:not(input)" ); + equal( label.text(), "", "Correct error label" ); +}); + +test( "Min and Max date set by attributes valid", function() { + var form = $( "#ranges" ), + name = $( "#rangeDateValid" ), + label; + + form.validate(); + form.get( 0 ).reset(); + name.valid(); + + label = $( "#ranges .error:not(input)" ); + equal( label.text(), "", "Correct error label" ); +}); + +test( "Min and Max strings set by attributes greater", function() { + var form = $( "#ranges" ), + name = $( "#rangeTextInvalidGreater" ), + label; + + form.validate(); + form.get( 0 ).reset(); + name.valid(); + + label = $( "#ranges .error:not(input)" ); + equal( label.text(), "Please enter a value less than or equal to 200.", "Correct error label" ); +}); + +test( "Min and Max strings set by attributes less", function() { + var form = $( "#ranges" ), + name = $( "#rangeTextInvalidLess" ), + label; + + form.validate(); + form.get( 0 ).reset(); + name.valid(); + + label = $( "#ranges .error:not(input)" ); + equal( label.text(), "Please enter a value greater than or equal to 200.", "Correct error label" ); +}); + +test( "Min and Max strings set by attributes valid", function() { + var form = $( "#ranges" ), + range = $( "#rangeTextValid" ), + label; + + form.validate(); + form.get( 0 ).reset(); + range.valid(); + + label = $( "#ranges .error:not(input)" ); + equal( label.text(), "", "Correct error label" ); +}); + +test( "Max set by data-rule, valid", function() { + var form = $( "#ranges" ), + range = $( "#rangeTextDataRuleValid" ), + label; + + form.validate(); + form.get( 0 ).reset(); + range.valid(); + + label = $( "#ranges .error:not(input)" ); + equal( label.text(), "", "Correct error label" ); +}); + +test( "calling blur on ignored element", function() { + var form = $( "#ignoredElements" ); + + form.validate({ + ignore: ".ignore", + submitHandler: $.noop, + invalidHandler: function() { + $( "#ss1" ).blur(); + } + }); + + form.trigger( "submit" ); + equal( form.valid(), false, "valid() should return false" ); +}); + +test( "Min and Max type absent set by attributes greater", function() { + var form = $( "#ranges" ), + name = $( "#rangeAbsentInvalidGreater" ), + label; + + form.validate(); + form.get( 0 ).reset(); + name.valid(); + + label = $( "#ranges .error:not(input)" ); + equal( label.text(), "Please enter a value less than or equal to 200.", "Correct error label" ); +}); + +test( "Min and Max type absent set by attributes less", function() { + var form = $( "#ranges" ), + name = $( "#rangeAbsentInvalidLess" ), + label; + + form.validate(); + form.get( 0 ).reset(); + name.valid(); + + label = $( "#ranges .error:not(input)" ); + equal( label.text(), "Please enter a value greater than or equal to 200.", "Correct error label" ); +}); + +test( "Min and Max type absent set by attributes valid", function() { + var form = $( "#ranges" ), + name = $( "#rangeAbsentValid" ), + label; + + form.validate(); + form.get( 0 ).reset(); + name.valid(); + + label = $( "#ranges .error:not(input)" ); + equal( label.text(), "", "Correct error label" ); +}); + +test( "Min and Max range set by attributes valid", function() { + // + // cannot test for overflow: + // When the element is suffering from an underflow, + // the user agent must set the element"s value to a valid + // floating-point number that represents the minimum. + // http://www.w3.org/TR/html5/forms.html#range-state-%28type=range%29 + // + var form = $( "#ranges" ), + name = $( "#rangeRangeValid" ), + label; + + form.validate(); + form.get( 0 ).reset(); + name.valid(); + + label = $( "#ranges .error:not(input)" ); + equal( label.text(), "", "Correct error label" ); +}); + +test( "Min and Max number set by attributes valid", function() { + var form = $( "#ranges" ), + name = $( "#rangeNumberValid" ), + label; + + form.validate(); + form.get( 0 ).reset(); + name.valid(); + + label = $( "#ranges .error:not(input)" ); + equal( label.text(), "", "Correct error label" ); +}); + +test( "Min and Max number set by attributes greater", function() { + var form = $( "#ranges" ), + name = $( "#rangeNumberInvalidGreater" ), + label; + + form.validate(); + form.get( 0 ).reset(); + name.valid(); + + label = $( "#ranges .error:not(input)" ); + equal( label.text(), "Please enter a value less than or equal to 200.", "Correct error label" ); +}); + +test( "Min and Max number set by attributes less", function() { + var form = $( "#ranges" ), + name = $( "#rangeNumberInvalidLess" ), + label; + + form.validate(); + form.get( 0 ).reset(); + name.valid(); + + label = $( "#ranges .error:not(input)" ); + equal( label.text(), "Please enter a value greater than or equal to 50.", "Correct error label" ); +}); + +test( "Rules allowed to have a value of zero invalid", function() { + var form = $( "#ranges" ), + name = $( "#rangeMinZeroInvalidLess" ), + label; + + form.validate(); + form.get( 0 ).reset(); + name.valid(); + + label = $( "#ranges .error:not(input)" ); + equal( label.text(), "Please enter a value greater than or equal to 0.", "Correct error label" ); +}); + +test( "Rules allowed to have a value of zero valid equal", function() { + var form = $( "#ranges" ), + name = $( "#rangeMinZeroValidEqual" ), + label; + + form.validate(); + form.get( 0 ).reset(); + name.valid(); + + label = $( "#ranges .error:not(input)" ); + equal( label.text(), "", "Correct error label" ); +}); + +test( "Rules allowed to have a value of zero valid greater", function() { + var form = $( "#ranges" ), + name = $( "#rangeMinZeroValidGreater" ), + label; + + form.validate(); + form.get( 0 ).reset(); + name.valid(); + + label = $( "#ranges .error:not(input)" ); + equal( label.text(), "", "Correct error label" ); +}); + +test( "Validation triggered on radio and checkbox via click", function() { + expect( 2 ); + + var form = $( "#radiocheckbox" ); + + // init validate + form.validate(); + + // validate so we have errors + ok( !form.valid(), "Form invalid"); + + // simulate native click on first checkbox to trigger change-event + $( "#radiocheckbox-0-1" ).simulate( "click" ); + + // simulate native click on first radio to trigger change-event + $( "#radiocheckbox-1-1" ).simulate( "click" ); + + // test if there is no error anymore + ok( form.find( "input.error" ).length === 0, "Form valid" ); +}); + +test( "destroy()", function() { + expect( 2 ); + + var form = $( "#form" ), + validate = form.validate(); + + strictEqual( $( form ).data( "validator" ), validate ); + + validate.destroy(); + strictEqual( $( form ).data( "validator" ), undefined ); +}); |