From 79b74e043a4ee737d44a95094ff1184e40bd5b16 Mon Sep 17 00:00:00 2001 From: =?utf8?q?Micha=C5=82=20Go=C5=82=C4=99biowski-Owczarek?= Date: Wed, 26 Jun 2019 21:39:10 +0200 Subject: [PATCH] Selector: Port Sizzle tests to jQuery Apart from porting most Sizzle tests to jQuery (mostly to its selector module), this commit fixes selector-native so that a jQuery custom compilation that excludes Sizzle passes all tests as well. Closes gh-4406 --- src/selector-native.js | 14 +- test/.eslintrc.json | 1 + test/data/qunit-fixture.html | 79 +- test/data/qunit-fixture.js | 2 +- test/data/selector/mixed_sort.html | 23 + test/data/testinit.js | 40 +- test/unit/attributes.js | 16 +- test/unit/core.js | 54 +- test/unit/manipulation.js | 2 +- test/unit/selector.js | 1736 ++++++++++++++++++++++++++-- test/unit/traversing.js | 77 +- 11 files changed, 1884 insertions(+), 160 deletions(-) create mode 100644 test/data/selector/mixed_sort.html diff --git a/src/selector-native.js b/src/selector-native.js index 20c37f4fa..511d85d03 100644 --- a/src/selector-native.js +++ b/src/selector-native.js @@ -34,6 +34,7 @@ define( [ */ var hasDuplicate, sortInput, + rhtmlSuffix = /HTML$/i, sortStable = jQuery.expando.split( "" ).sort( sortOrder ).join( "" ) === jQuery.expando, // Support: IE 9 - 11+ @@ -199,11 +200,14 @@ jQuery.extend( { return a === bup || !!( bup && bup.nodeType === 1 && adown.contains( bup ) ); }, isXMLDoc: function( elem ) { - - // documentElement is verified for cases where it doesn't yet exist - // (such as loading iframes in IE - #4833) - var documentElement = elem && ( elem.ownerDocument || elem ).documentElement; - return documentElement ? documentElement.nodeName !== "HTML" : false; + var namespace = elem.namespaceURI, + documentElement = ( elem.ownerDocument || elem ).documentElement; + + // Assume HTML when documentElement doesn't yet exist, such as inside + // document fragments. + return !rhtmlSuffix.test( namespace || + documentElement && documentElement.nodeName || + "HTML" ); }, expr: { attrHandle: {}, diff --git a/test/.eslintrc.json b/test/.eslintrc.json index a23ea5f98..a6d8fc872 100644 --- a/test/.eslintrc.json +++ b/test/.eslintrc.json @@ -18,6 +18,7 @@ "ajaxTest": false, "testIframe": false, "createDashboardXML": false, + "createWithFriesXML": false, "createXMLFragment": false, "moduleTeardown": false, "url": false, diff --git a/test/data/qunit-fixture.html b/test/data/qunit-fixture.html index e0fd3e60e..7564311a3 100644 --- a/test/data/qunit-fixture.html +++ b/test/data/qunit-fixture.html @@ -1,8 +1,8 @@

See this blog entry for more information.

- Here are some links in a normal paragraph: Google, + Here are some [links] in a normal paragraph: Google, Google Groups (Link). - This link has class="blog": + This link has class="blog": diveintomark

@@ -78,11 +78,11 @@ - + - + "'台北Táiběi"' - + test element @@ -163,7 +163,26 @@ Z
+
+ +
+
+ + +
+
+ + +
C
+
+
+ + + + + +
@@ -194,6 +213,53 @@ Z
+
+
+ + + + + + Neither enabled nor disabled +
+
+ + + + + + + + + + Neither enabled nor disabled +
+ + +
+
1 2 @@ -208,7 +274,7 @@ Z
-
fadeIn
fadeIn
+
fadeIn
fadeIn
fadeOut
fadeOut
show
show
@@ -235,3 +301,4 @@ Z
+
diff --git a/test/data/qunit-fixture.js b/test/data/qunit-fixture.js index 46f0c3c26..54e57f408 100644 --- a/test/data/qunit-fixture.js +++ b/test/data/qunit-fixture.js @@ -1,2 +1,2 @@ // Generated by build/tasks/qunit_fixture.js -QUnit.config.fixture = "

See this blog entry for more information.

\n

\n\tHere are some links in a normal paragraph: Google,\n\tGoogle Groups (Link).\n\tThis link has class=\"blog\":\n\tdiveintomark\n\n

\n
\n\t

Everything inside the red border is inside a div with id=\"foo\".

\n\t

This is a normal link: Yahoo

\n\t

This link has class=\"blog\": Simon Willison's Weblog

\n\n
\n
\n\t
\n
\n\n

Try them out:

\n\n
    \n
    \n\t\n\t\n\t\n\t\n\n\t\n\t\n\t\n\n\t\n\t\n\n\t\n\t\n\n\t\n\n\t\n\n\t\n\t\n\t\n\t\n\t\n\n\t\n\t\t\n\t\t\n\t\n\n\t\n\t\n\t\n\t\n\t\n\t\n\n\ttest element\n
    \nFloat test.\n\n
    \n\t\n\t\n
    \n
    \n\n
    \n\t\n\t\n\t\n\t\n
    \n\n
    \n\t\n\t\n\t\n\t\n\t\n\t\n\t\n\t\n\t\n\t\n\t\n\t\n\t\n\t\n\t\n\t\n\t\n\t\n\t\n\t\n\t\n\t\n\t\n\t\n\t\n\t\n\t\t\n\t\n
    \n
    \n\t
    \n\t\t
    \n\t\t\t\n\t\t\t\n\t\t\t\n\t\t\t\n\t\t
    \n\t
    \n\t
    hi there
    \n\t
    \n\t\t
    hidden
    \n\t
    \n\t
    \n\t\t
    \n\t
    \n\t
    \n
    \n\n
    \n\t
      \n\t\t
    1. Rice
    2. \n\t\t
    3. Beans
    4. \n\t\t
    5. Blinis
    6. \n\t\t
    7. Tofu
    8. \n\t
    \n\n\t
    I'm hungry. I should...
    \n\t...Eat lots of food... |\n\t...Eat a little food... |\n\t...Eat no food...\n\t...Eat a burger...\n\t...Eat some funyuns...\n\t...Eat some funyuns...\n\t\n\t\n\t\n\t\n\t\t\n\t\n
    \n\n
    \n\t\n\t\n
    \n\n
    \n\t1\n\t2\n\t\n\t\t\n\t\t\t\n\t\t\t\t\n\t\t\t\n\t\t\n\t\n\t\n
    \n
    \n\t
    \n\t\t
    fadeIn
    fadeIn
    \n\t\t
    fadeOut
    fadeOut
    \n\n\t\t
    show
    show
    \n\t\t
    hide
    hide
    \n\t\t
    hide
    hide
    \n\n\t\t
    togglein
    togglein
    \n\t\t
    toggleout
    toggleout
    \n\t\t
    toggleout
    toggleout
    \n\n\t\t
    slideUp
    slideUp
    \n\t\t
    slideDown
    slideDown
    \n\t\t
    slideUp
    slideUp
    \n\n\t\t
    slideToggleIn
    slideToggleIn
    \n\t\t
    slideToggleOut
    slideToggleOut
    \n\n\t\t
    fadeToggleIn
    fadeToggleIn
    \n\t\t
    fadeToggleOut
    fadeToggleOut
    \n\n\t\t
    fadeTo
    fadeTo
    \n\t
    \n\n\t
    \n\t\n
    \n"; +QUnit.config.fixture = "

    See this blog entry for more information.

    \n

    \n\tHere are some [links] in a normal paragraph: Google,\n\tGoogle Groups (Link).\n\tThis link has class=\"blog\":\n\tdiveintomark\n\n

    \n
    \n\t

    Everything inside the red border is inside a div with id=\"foo\".

    \n\t

    This is a normal link: Yahoo

    \n\t

    This link has class=\"blog\": Simon Willison's Weblog

    \n\n
    \n
    \n\t
    \n
    \n\n

    Try them out:

    \n\n
      \n
      \n\t\n\t\n\t\n\t\n\n\t\n\t\n\t\n\n\t\n\t\n\n\t\n\t\n\n\t\n\n\t\n\n\t\n\t\n\t\n\t\n\t\n\n\t\n\t\t\n\t\t\n\t\n\n\t\n\t\n\t\"'台北Táiběi\"'\n\t\n\t\n\t\n\n\ttest element\n
      \nFloat test.\n\n
      \n\t\n\t\n
      \n
      \n\n
      \n\t\n\t\n\t\n\t\n
      \n\n
      \n\t\n\t\n\t\n\t\n\t\n\t\n\t\n\t\n\t\n\t\n\t\n\t\n\t\n\t\n\t\n\t\n\t\n\t\n\t\n\t\n\t\n\t\n\t\n\t\n\t\n\t\n\t\t\n\t\n
      \n
      \n\t
      \n\t\t
      \n\t\t\t\n\t\t\t\n\t\t\t\n\t\t\t\n\t\t
      \n\t
      \n\t
      hi there
      \n\t
      \n\t\t
      hidden
      \n\t
      \n\t
      \n\t\t
      \n\t
      \n\t
      \n\t\t\n\t
      \n\t
      \n\t
      \n\t\t\n\t\t\n\t
      \n\t
      \n\t\t\n\t\t\n\t\t
      C
      \n\t
      \n\t
      \n\t\t\n\t\t\n\t\t\n\t\t\n\t\t\n\t
      \n
      \n\n
      \n\t
        \n\t\t
      1. Rice
      2. \n\t\t
      3. Beans
      4. \n\t\t
      5. Blinis
      6. \n\t\t
      7. Tofu
      8. \n\t
      \n\n\t
      I'm hungry. I should...
      \n\t...Eat lots of food... |\n\t...Eat a little food... |\n\t...Eat no food...\n\t...Eat a burger...\n\t...Eat some funyuns...\n\t...Eat some funyuns...\n\t\n\t\n\t\n\t\n\t\t\n\t\n
      \n\n
      \n\t\n\t\n
      \n\n
      \n\t
      \n\t\t\n\t\t\n\t\t\n\t\t\n\t\t\n\t\tNeither enabled nor disabled\n\t
      \n\t
      \n\t\t\n\t\t\n\t\t\n\t\t\n\t\t\n\t\t\n\t\t\n\t\t\n\t\t\n\t\tNeither enabled nor disabled\n\t
      \n\t\n\t\n
      \n\n
      \n\t1\n\t2\n\t\n\t\t\n\t\t\t\n\t\t\t\t\n\t\t\t\n\t\t\n\t\n\t\n
      \n
      \n\t
      \n\t\t
      fadeIn
      fadeIn
      \n\t\t
      fadeOut
      fadeOut
      \n\n\t\t
      show
      show
      \n\t\t
      hide
      hide
      \n\t\t
      hide
      hide
      \n\n\t\t
      togglein
      togglein
      \n\t\t
      toggleout
      toggleout
      \n\t\t
      toggleout
      toggleout
      \n\n\t\t
      slideUp
      slideUp
      \n\t\t
      slideDown
      slideDown
      \n\t\t
      slideUp
      slideUp
      \n\n\t\t
      slideToggleIn
      slideToggleIn
      \n\t\t
      slideToggleOut
      slideToggleOut
      \n\n\t\t
      fadeToggleIn
      fadeToggleIn
      \n\t\t
      fadeToggleOut
      fadeToggleOut
      \n\n\t\t
      fadeTo
      fadeTo
      \n\t
      \n\n\t
      \n\t\n
      \n
      \n"; diff --git a/test/data/selector/mixed_sort.html b/test/data/selector/mixed_sort.html new file mode 100644 index 000000000..090b9aaca --- /dev/null +++ b/test/data/selector/mixed_sort.html @@ -0,0 +1,23 @@ + + + + + jQuery selector - cross-window uniqueSort + + + + + + + + diff --git a/test/data/testinit.js b/test/data/testinit.js index cb600cf15..39d8d1d7b 100644 --- a/test/data/testinit.js +++ b/test/data/testinit.js @@ -51,15 +51,9 @@ this.q = function() { * @example match("Check for something", "p", ["foo", "bar"]); */ function match( message, selector, expectedIds, context, assert ) { - var f = jQuery( selector, context ).get(), - s = "", - i = 0; - - for ( ; i < f.length; i++ ) { - s += ( s && "," ) + "\"" + f[ i ].id + "\""; - } + var elems = jQuery( selector, context ).get(); - assert.deepEqual( f, q.apply( q, expectedIds ), message + " (" + selector + ")" ); + assert.deepEqual( elems, q.apply( q, expectedIds ), message + " (" + selector + ")" ); } /** @@ -102,6 +96,36 @@ this.createDashboardXML = function() { return jQuery.parseXML( string ); }; +this.createWithFriesXML = function() { + var string = " \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + 1 \ + \ + \ + \ + \ + foo \ + \ + \ + \ + \ + \ + \ + "; + + return jQuery.parseXML( string ); +}; + this.createXMLFragment = function() { var frag, xml = document.implementation.createDocument( "", "", null ); diff --git a/test/unit/attributes.js b/test/unit/attributes.js index 1e176b22e..5f58cf1e7 100644 --- a/test/unit/attributes.js +++ b/test/unit/attributes.js @@ -1278,7 +1278,7 @@ QUnit.test( "addClass(Array)", function( assert ) { } ); QUnit.test( "addClass(Function) with incoming value", function( assert ) { - assert.expect( 52 ); + assert.expect( 57 ); var pass, i, div = jQuery( "#qunit-fixture div" ), old = div.map( function() { @@ -1286,10 +1286,8 @@ QUnit.test( "addClass(Function) with incoming value", function( assert ) { } ); div.addClass( function( i, val ) { - if ( this.id !== "_firebugConsole" ) { - assert.equal( val, old[ i ], "Make sure the incoming value is correct." ); - return "test"; - } + assert.equal( val, old[ i ], "Make sure the incoming value is correct." ); + return "test"; } ); pass = true; @@ -1355,17 +1353,15 @@ QUnit.test( "removeClass(Array) - simple", function( assert ) { } ); QUnit.test( "removeClass(Function) with incoming value", function( assert ) { - assert.expect( 52 ); + assert.expect( 57 ); var $divs = jQuery( "#qunit-fixture div" ).addClass( "test" ), old = $divs.map( function() { return jQuery( this ).attr( "class" ); } ); $divs.removeClass( function( i, val ) { - if ( this.id !== "_firebugConsole" ) { - assert.equal( val, old[ i ], "Make sure the incoming value is correct." ); - return "test"; - } + assert.equal( val, old[ i ], "Make sure the incoming value is correct." ); + return "test"; } ); assert.ok( !$divs.is( ".test" ), "Remove Class" ); diff --git a/test/unit/core.js b/test/unit/core.js index df71caa26..0c96e23ad 100644 --- a/test/unit/core.js +++ b/test/unit/core.js @@ -380,6 +380,52 @@ QUnit.test( "isXMLDoc - HTML", function( assert ) { document.body.removeChild( iframe ); } ); +QUnit.test( "isXMLDoc - embedded SVG", function( assert ) { + assert.expect( 6 ); + + var htmlTree = jQuery( "
      " + + "" + + "" + + "" + + "
      " + )[ 0 ]; + + assert.strictEqual( jQuery.isXMLDoc( htmlTree ), false, "disconnected div element" ); + assert.strictEqual( jQuery.isXMLDoc( htmlTree.firstChild ), true, + "disconnected HTML-embedded SVG root element" ); + + assert.strictEqual( jQuery.isXMLDoc( htmlTree.firstChild.firstChild ), true, + "disconnected HTML-embedded SVG child element" ); + + document.getElementById( "qunit-fixture" ).appendChild( htmlTree ); + assert.strictEqual( jQuery.isXMLDoc( htmlTree ), false, "connected div element" ); + assert.strictEqual( jQuery.isXMLDoc( htmlTree.firstChild ), true, + "connected HTML-embedded SVG root element" ); + + assert.strictEqual( jQuery.isXMLDoc( htmlTree.firstChild.firstChild ), true, + "disconnected HTML-embedded SVG child element" ); +} ); + +QUnit.test( "isXMLDoc - XML", function( assert ) { + assert.expect( 8 ); + + var xml = createDashboardXML(); + var svg = jQuery.parseXML( + "" + + "" + ); + assert.ok( jQuery.isXMLDoc( xml ), "XML document" ); + assert.ok( jQuery.isXMLDoc( xml.documentElement ), "XML documentElement" ); + assert.ok( jQuery.isXMLDoc( xml.documentElement.firstChild ), "XML child element" ); + assert.ok( jQuery.isXMLDoc( jQuery( "tab", xml )[ 0 ] ), "XML tab Element" ); + + assert.ok( jQuery.isXMLDoc( svg ), "SVG document" ); + assert.ok( jQuery.isXMLDoc( svg.documentElement ), "SVG documentElement" ); + assert.ok( jQuery.isXMLDoc( svg.documentElement.firstChild ), "SVG child element" ); + assert.ok( jQuery.isXMLDoc( jQuery( "desc", svg )[ 0 ] ), "XML desc Element" ); +} ); + QUnit.test( "XSS via location.hash", function( assert ) { var done = assert.async(); assert.expect( 1 ); @@ -399,14 +445,6 @@ QUnit.test( "XSS via location.hash", function( assert ) { } } ); -QUnit.test( "isXMLDoc - XML", function( assert ) { - assert.expect( 3 ); - var xml = createDashboardXML(); - assert.ok( jQuery.isXMLDoc( xml ), "XML document" ); - assert.ok( jQuery.isXMLDoc( xml.documentElement ), "XML documentElement" ); - assert.ok( jQuery.isXMLDoc( jQuery( "tab", xml )[ 0 ] ), "XML Tab Element" ); -} ); - QUnit.test( "jQuery('html')", function( assert ) { assert.expect( 18 ); diff --git a/test/unit/manipulation.js b/test/unit/manipulation.js index 2ba802656..68e7f8e14 100644 --- a/test/unit/manipulation.js +++ b/test/unit/manipulation.js @@ -193,7 +193,7 @@ function testAppendForObject( valueObj, isFragment, assert ) { function testAppend( valueObj, assert ) { - assert.expect( 78 ); + assert.expect( 82 ); testAppendForObject( valueObj, false, assert ); testAppendForObject( valueObj, true, assert ); diff --git a/test/unit/selector.js b/test/unit/selector.js index f9c6ffc7a..086b49fe5 100644 --- a/test/unit/selector.js +++ b/test/unit/selector.js @@ -1,12 +1,55 @@ -QUnit.module( "selector", { afterEach: moduleTeardown } ); +QUnit.module( "selector", { + beforeEach: function() { + this.safari = /\bsafari\b/i.test( navigator.userAgent ) && + !/\bchrome\b/i.test( navigator.userAgent ); + this.chrome = /\bchrome\b/i.test( navigator.userAgent ) && + !/\bedge\b/i.test( navigator.userAgent ); + }, + afterEach: moduleTeardown +} ); + +QUnit.test( "empty", function( assert ) { + assert.expect( 5 ); + + var form; + + assert.strictEqual( jQuery.find( "" ).length, 0, + "Empty selector returns an empty array" ); + + assert.deepEqual( jQuery.find( "div", document.createTextNode( "" ) ), [], + "Text element as context fails silently" ); + form = document.getElementById( "form" ); + assert.ok( !jQuery( form ).is( "" ), "Empty string passed to .is() does not match" ); + + if ( jQuery.find.compile ) { + assert.equal( jQuery.find( " " ).length, 0, "Empty selector returns an empty array" ); + assert.equal( jQuery.find( "\t" ).length, 0, "Empty selector returns an empty array" ); + } else { + assert.ok( "skip", "whitespace-only selector not supported in selector-native" ); + assert.ok( "skip", "whitespace-only selector not supported in selector-native" ); + } +} ); + +QUnit.test( "star", function( assert ) { + assert.expect( 2 ); -/** - * This test page is for selector tests that require jQuery in order to do the selection - */ + var good, i; + var all = jQuery.find( "*" ); + + assert.ok( all.length >= 30, "Select all" ); + good = true; + for ( i = 0; i < all.length; i++ ) { + if ( all[ i ].nodeType === 8 ) { + good = false; + } + } + assert.ok( good, "Select all elements, no comment nodes" ); +} ); QUnit.test( "element", function( assert ) { - assert.expect( 7 ); + assert.expect( 37 ); + var i, lengthtest, siblingTest, html; var fixture = document.getElementById( "qunit-fixture" ); assert.deepEqual( jQuery( "p", fixture ).get(), q( "firstp", "ap", "sndp", "en", "sap", "first" ), "Finding elements with a Node context." ); @@ -19,12 +62,174 @@ QUnit.test( "element", function( assert ) { // #7533 assert.equal( jQuery( "

      foo

      " ).find( "p" ).length, 1, "Find where context root is a node and has an ID with CSS3 meta characters" ); + + assert.equal( jQuery.find( "" ).length, 0, "Empty selector returns an empty array" ); + assert.deepEqual( jQuery.find( "div", document.createTextNode( "" ) ), [], + "Text element as context fails silently" ); + + assert.t( "Element Selector", "html", [ "html" ] ); + assert.t( "Element Selector", "body", [ "body" ] ); + assert.t( "Element Selector", "#qunit-fixture p", [ "firstp", "ap", "sndp", "en", "sap", "first" ] ); + + assert.t( "Leading space", " #qunit-fixture p", [ "firstp", "ap", "sndp", "en", "sap", "first" ] ); + assert.t( "Leading tab", "\t#qunit-fixture p", [ "firstp", "ap", "sndp", "en", "sap", "first" ] ); + assert.t( "Leading carriage return", "\r#qunit-fixture p", [ "firstp", "ap", "sndp", "en", "sap", "first" ] ); + assert.t( "Leading line feed", "\n#qunit-fixture p", [ "firstp", "ap", "sndp", "en", "sap", "first" ] ); + assert.t( "Leading form feed", "\f#qunit-fixture p", [ "firstp", "ap", "sndp", "en", "sap", "first" ] ); + assert.t( "Trailing space", "#qunit-fixture p ", [ "firstp", "ap", "sndp", "en", "sap", "first" ] ); + assert.t( "Trailing tab", "#qunit-fixture p\t", [ "firstp", "ap", "sndp", "en", "sap", "first" ] ); + assert.t( "Trailing carriage return", "#qunit-fixture p\r", + [ "firstp", "ap", "sndp", "en", "sap", "first" ] ); + assert.t( "Trailing line feed", "#qunit-fixture p\n", [ "firstp", "ap", "sndp", "en", "sap", "first" ] ); + assert.t( "Trailing form feed", "#qunit-fixture p\f", [ "firstp", "ap", "sndp", "en", "sap", "first" ] ); + + assert.deepEqual( + jQuery( jQuery.find( "div ol" ) ).filter( "#qunit-fixture *" ).get(), + q( "empty", "listWithTabIndex" ), + "Parent Element" + ); + assert.deepEqual( + jQuery( jQuery.find( "div\tol" ) ).filter( "#qunit-fixture *" ).get(), + q( "empty", "listWithTabIndex" ), + "Parent Element (non-space descendant combinator)" + ); + + // Check for unique-ness and sort order + assert.deepEqual( jQuery.find( "p, div p" ), jQuery.find( "p" ), "Check for duplicates: p, div p" ); + + jQuery( "

      " ).prependTo( "#qunit-fixture" ); + assert.t( "Checking sort order", "#qunit-fixture h2, #qunit-fixture h1", [ "h1", "h2", "h2-2" ] ); + + if ( jQuery.find.compile ) { + assert.t( "Checking sort order", "#qunit-fixture h2:first, #qunit-fixture h1:first", [ "h1", "h2" ] ); + } else { + assert.ok( "skip", ":first not supported in selector-native" ); + } + + assert.t( "Checking sort order", "#qunit-fixture p, #qunit-fixture p a", + [ "firstp", "simon1", "ap", "google", "groups", "anchor1", "mark", "sndp", "en", "yahoo", + "sap", "anchor2", "simon", "first" ] ); + + // Test Conflict ID + lengthtest = document.getElementById( "lengthtest" ); + assert.deepEqual( jQuery.find( "#idTest", lengthtest ), q( "idTest" ), + "Finding element with id of ID." ); + assert.deepEqual( jQuery.find( "[name='id']", lengthtest ), q( "idTest" ), + "Finding element with id of ID." ); + assert.deepEqual( jQuery.find( "input[id='idTest']", lengthtest ), q( "idTest" ), + "Finding elements with id of ID." ); + + if ( jQuery.find.compile ) { + siblingTest = document.getElementById( "siblingTest" ); + assert.deepEqual( jQuery.find( "div em", siblingTest ), [], + "Element-rooted QSA does not select based on document context" ); + assert.deepEqual( jQuery.find( "div em, div em, div em:not(div em)", siblingTest ), [], + "Element-rooted QSA does not select based on document context" ); + assert.deepEqual( jQuery.find( "div em, em\\,", siblingTest ), [], + "Escaped commas do not get treated with an id in element-rooted QSA" ); + } else { + assert.ok( "skip", "Element-rooted QSA behavior different in selector-native" ); + assert.ok( "skip", "Element-rooted QSA behavior different in selector-native" ); + assert.ok( "skip", "Element-rooted QSA behavior different in selector-native" ); + } + + html = ""; + for ( i = 0; i < 100; i++ ) { + html = "
      " + html + "
      "; + } + html = jQuery( html ).appendTo( document.body ); + assert.ok( !!jQuery.find( "body div div div" ).length, + "No stack or performance problems with large amounts of descendants" ); + assert.ok( !!jQuery.find( "body>div div div" ).length, + "No stack or performance problems with large amounts of descendants" ); + html.remove(); + + // Real use case would be using .watch in browsers with window.watch (see Issue #157) + q( "qunit-fixture" )[ 0 ].appendChild( document.createElement( "toString" ) ).id = "toString"; + assert.t( "Element name matches Object.prototype property", "toString#toString", [ "toString" ] ); +} ); + +QUnit.test( "XML Document Selectors", function( assert ) { + assert.expect( 11 ); + + var xml = createWithFriesXML(); + + assert.equal( jQuery.find( "foo_bar", xml ).length, 1, "Element Selector with underscore" ); + assert.equal( jQuery.find( ".component", xml ).length, 1, "Class selector" ); + assert.equal( jQuery.find( "[class*=component]", xml ).length, 1, "Attribute selector for class" ); + assert.equal( jQuery.find( "property[name=prop2]", xml ).length, 1, "Attribute selector with name" ); + assert.equal( jQuery.find( "[name=prop2]", xml ).length, 1, "Attribute selector with name" ); + assert.equal( jQuery.find( "#seite1", xml ).length, 1, "Attribute selector with ID" ); + assert.equal( jQuery.find( "component#seite1", xml ).length, 1, "Attribute selector with ID" ); + assert.equal( jQuery( "component", xml ).filter( "#seite1" ).length, 1, + "Attribute selector filter with ID" ); + assert.equal( jQuery.find( "meta property thing", xml ).length, 2, + "Descendent selector and dir caching" ); + if ( jQuery.find.compile ) { + assert.ok( jQuery( xml.lastChild ).is( "soap\\:Envelope" ), "Check for namespaced element" ); + + xml = jQuery.parseXML( "" ); + + assert.equal( jQuery.find( "elem:not(:has(*))", xml ).length, 1, + "Non-qSA path correctly handles numeric ids (jQuery #14142)" ); + } else { + assert.ok( "skip", "namespaced elements not matching correctly in selector-native" ); + assert.ok( "skip", ":not(complex selector) not supported in selector-native" ); + } +} ); + +QUnit.test( "broken selectors throw", function( assert ) { + assert.expect( 31 ); + + function broken( name, selector ) { + assert.throws( function() { + jQuery.find( selector ); + }, name + ": " + selector ); + } + + broken( "Broken Selector", "[" ); + broken( "Broken Selector", "(" ); + broken( "Broken Selector", "{" ); + broken( "Broken Selector", "<" ); + broken( "Broken Selector", "()" ); + broken( "Broken Selector", "<>" ); + broken( "Broken Selector", "{}" ); + broken( "Broken Selector", "," ); + broken( "Broken Selector", ",a" ); + broken( "Broken Selector", "a," ); + broken( "Broken Selector", "[id=012345678901234567890123456789" ); + broken( "Doesn't exist", ":visble" ); + broken( "Nth-child", ":nth-child" ); + broken( "Nth-child", ":nth-child(-)" ); + broken( "Nth-child", ":nth-child(asdf)", [] ); + broken( "Nth-child", ":nth-child(2n+-0)" ); + broken( "Nth-child", ":nth-child(2+0)" ); + broken( "Nth-child", ":nth-child(- 1n)" ); + broken( "Nth-child", ":nth-child(-1 n)" ); + broken( "First-child", ":first-child(n)" ); + broken( "Last-child", ":last-child(n)" ); + broken( "Only-child", ":only-child(n)" ); + broken( "Nth-last-last-child", ":nth-last-last-child(1)" ); + broken( "First-last-child", ":first-last-child" ); + broken( "Last-last-child", ":last-last-child" ); + broken( "Only-last-child", ":only-last-child" ); + + // Make sure attribute value quoting works correctly. See: #6093 + jQuery( "" + + "" ) + .appendTo( "#qunit-fixture" ); + + broken( "Attribute equals non-value", "input[name=]" ); + broken( "Attribute equals unquoted non-identifer", "input[name=foo.baz]" ); + broken( "Attribute equals unquoted non-identifer", "input[name=foo[baz]]" ); + broken( "Attribute equals bad string", "input[name=''double-quoted'']" ); + broken( "Attribute equals bad string", "input[name='apostrophe'd']" ); } ); QUnit.test( "id", function( assert ) { - assert.expect( 26 ); + assert.expect( 34 ); - var a; + var fiddle, a; assert.t( "ID Selector", "#body", [ "body" ] ); assert.t( "ID Selector w/ Element", "body#body", [ "body" ] ); @@ -37,12 +242,31 @@ QUnit.test( "id", function( assert ) { assert.t( "Child ID selector using UTF8", "form > #台北", [ "台北" ] ); assert.t( "Escaped ID", "#foo\\:bar", [ "foo:bar" ] ); + + if ( jQuery.find.compile ) { + assert.t( "Escaped ID with descendant", "#foo\\:bar span:not(:input)", [ "foo_descendant" ] ); + } else { + assert.ok( "skip", ":input not supported in selector-native" ); + } + assert.t( "Escaped ID", "#test\\.foo\\[5\\]bar", [ "test.foo[5]bar" ] ); assert.t( "Descendant escaped ID", "div #foo\\:bar", [ "foo:bar" ] ); assert.t( "Descendant escaped ID", "div #test\\.foo\\[5\\]bar", [ "test.foo[5]bar" ] ); assert.t( "Child escaped ID", "form > #foo\\:bar", [ "foo:bar" ] ); assert.t( "Child escaped ID", "form > #test\\.foo\\[5\\]bar", [ "test.foo[5]bar" ] ); + fiddle = jQuery( "
      " ) + .appendTo( "#qunit-fixture" ); + + if ( jQuery.find.compile ) { + assert.deepEqual( jQuery.find( "> span", jQuery( "#fiddle\\\\Foo" )[ 0 ] ), + q( [ "fiddleSpan" ] ), "Escaped ID as context" ); + } else { + assert.ok( "skip", "leading > not supported in selector-native" ); + } + + fiddle.remove(); + assert.t( "ID Selector, child ID present", "#form > #radio1", [ "radio1" ] ); // bug #267 assert.t( "ID Selector, not an ancestor ID", "#form #first", [] ); assert.t( "ID Selector, not a child ID", "#form > #option1a", [] ); @@ -50,13 +274,25 @@ QUnit.test( "id", function( assert ) { assert.t( "All Children of ID", "#foo > *", [ "sndp", "en", "sap" ] ); assert.t( "All Children of ID with no children", "#firstUL > *", [] ); + assert.equal( jQuery( "#tName1" )[ 0 ].id, "tName1", + "ID selector with same value for a name attribute" ); + assert.t( "ID selector non-existing but name attribute on an A tag", "#tName2", [] ); + assert.t( "Leading ID selector non-existing but name attribute on an A tag", "#tName2 span", [] ); + assert.t( "Leading ID selector existing, retrieving the child", "#tName1 span", [ "tName1-span" ] ); + assert.equal( jQuery( "div > div #tName1" )[ 0 ].id, jQuery( "#tName1-span" )[ 0 ].parentNode.id, + "Ending with ID" ); + a = jQuery( "" ).appendTo( "#qunit-fixture" ); assert.t( "ID Selector contains backslash", "#backslash\\\\foo", [ "backslash\\foo" ] ); + a.remove(); assert.t( "ID Selector on Form with an input that has a name of 'id'", "#lengthtest", [ "lengthtest" ] ); assert.t( "ID selector with non-existent ancestor", "#asdfasdf #foobar", [] ); // bug #986 + assert.deepEqual( jQuery.find( "div#form", document.body ), [], + "ID selector within the context of another element" ); + assert.t( "Underscore ID", "#types_all", [ "types_all" ] ); assert.t( "Dash ID", "#qunit-fixture", [ "qunit-fixture" ] ); @@ -64,16 +300,72 @@ QUnit.test( "id", function( assert ) { } ); QUnit.test( "class", function( assert ) { - assert.expect( 4 ); - - assert.deepEqual( jQuery( ".blog", document.getElementsByTagName( "p" ) ).get(), q( "mark", "simon" ), "Finding elements with a context." ); - assert.deepEqual( jQuery( ".blog", "p" ).get(), q( "mark", "simon" ), "Finding elements with a context." ); - assert.deepEqual( jQuery( ".blog", jQuery( "p" ) ).get(), q( "mark", "simon" ), "Finding elements with a context." ); - assert.deepEqual( jQuery( "p" ).find( ".blog" ).get(), q( "mark", "simon" ), "Finding elements with a context." ); + assert.expect( 32 ); + + assert.deepEqual( jQuery( ".blog", document.getElementsByTagName( "p" ) ).get(), + q( "mark", "simon" ), "Finding elements with a context." ); + assert.deepEqual( jQuery( ".blog", "p" ).get(), + q( "mark", "simon" ), "Finding elements with a context." ); + assert.deepEqual( jQuery( ".blog", jQuery( "p" ) ).get(), + q( "mark", "simon" ), "Finding elements with a context." ); + assert.deepEqual( jQuery( "p" ).find( ".blog" ).get(), + q( "mark", "simon" ), "Finding elements with a context." ); + + assert.t( "Class Selector", ".blog", [ "mark", "simon" ] ); + assert.t( "Class Selector", ".GROUPS", [ "groups" ] ); + assert.t( "Class Selector", ".blog.link", [ "simon" ] ); + assert.t( "Class Selector w/ Element", "a.blog", [ "mark", "simon" ] ); + assert.t( "Parent Class Selector", "p .blog", [ "mark", "simon" ] ); + + assert.t( "Class selector using UTF8", ".台北Táiběi", [ "utf8class1" ] ); + assert.t( "Class selector using UTF8", ".台北", [ "utf8class1", "utf8class2" ] ); + assert.t( "Class selector using UTF8", ".台北Táiběi.台北", [ "utf8class1" ] ); + assert.t( "Class selector using UTF8", ".台北Táiběi, .台北", [ "utf8class1", "utf8class2" ] ); + assert.t( "Descendant class selector using UTF8", "div .台北Táiběi", [ "utf8class1" ] ); + assert.t( "Child class selector using UTF8", "form > .台北Táiběi", [ "utf8class1" ] ); + + assert.t( "Escaped Class", ".foo\\:bar", [ "foo:bar" ] ); + assert.t( "Escaped Class", ".test\\.foo\\[5\\]bar", [ "test.foo[5]bar" ] ); + assert.t( "Descendant escaped Class", "div .foo\\:bar", [ "foo:bar" ] ); + assert.t( "Descendant escaped Class", "div .test\\.foo\\[5\\]bar", [ "test.foo[5]bar" ] ); + assert.t( "Child escaped Class", "form > .foo\\:bar", [ "foo:bar" ] ); + assert.t( "Child escaped Class", "form > .test\\.foo\\[5\\]bar", [ "test.foo[5]bar" ] ); + + var div = document.createElement( "div" ); + div.innerHTML = "
      "; + assert.deepEqual( jQuery.find( ".e", div ), [ div.firstChild ], "Finding a second class." ); + + div.lastChild.className = "e"; + + assert.ok( !jQuery( div ).is( ".null" ), + ".null does not match an element with no class" ); + assert.ok( !jQuery( div.firstChild ).is( ".null div" ), + ".null does not match an element with no class" ); + div.className = "null"; + assert.ok( jQuery( div ).is( ".null" ), ".null matches element with class 'null'" ); + assert.ok( jQuery( div.firstChild ).is( ".null div" ), + "caching system respects DOM changes" ); + assert.ok( !jQuery( document ).is( ".foo" ), + "testing class on document doesn't error" ); + assert.ok( !jQuery( window ).is( ".foo" ), "testing class on window doesn't error" ); + + assert.deepEqual( jQuery.find( ".e", div ), [ div.firstChild, div.lastChild ], + "Finding a modified class." ); + + div.lastChild.className += " hasOwnProperty toString"; + assert.deepEqual( jQuery.find( ".e.hasOwnProperty.toString", div ), [ div.lastChild ], + "Classes match Object.prototype properties" ); + + div = jQuery( "
      " )[ 0 ]; + assert.equal( jQuery.find( ".foo", div ).length, 1, "Class selector against SVG container" ); + assert.equal( jQuery.find( ".foo", div.firstChild ).length, 1, + "Class selector directly against SVG" ); } ); QUnit.test( "name", function( assert ) { - assert.expect( 5 ); + assert.expect( 14 ); var form; @@ -81,15 +373,32 @@ QUnit.test( "name", function( assert ) { assert.t( "Name selector with single quotes", "input[name='action']", [ "text1" ] ); assert.t( "Name selector with double quotes", "input[name=\"action\"]", [ "text1" ] ); + assert.t( "Name selector non-input", "[name=example]", [ "name-is-example" ] ); + assert.t( "Name selector non-input", "[name=div]", [ "name-is-div" ] ); + assert.t( "Name selector non-input", "*[name=iframe]", [ "iframe" ] ); + assert.t( "Name selector for grouped input", "input[name='types[]']", [ "types_all", "types_anime", "types_movie" ] ); + form = document.getElementById( "form" ); + assert.deepEqual( jQuery.find( "input[name=action]", form ), q( "text1" ), + "Name selector within the context of another element" ); + assert.deepEqual( jQuery.find( "input[name='foo[bar]']", form ), q( "hidden2" ), + "Name selector for grouped form element within the context of another element" ); + form = jQuery( "
      " ).appendTo( "body" ); - assert.equal( jQuery( "input", form[ 0 ] ).length, 1, "Make sure that rooted queries on forms (with possible expandos) work." ); + assert.equal( jQuery.find( "input", form[ 0 ] ).length, 1, + "Make sure that rooted queries on forms (with possible expandos) work." ); form.remove(); + + assert.t( "Find elements that have similar IDs", "[name=tName1]", [ "tName1ID" ] ); + assert.t( "Find elements that have similar IDs", "[name=tName2]", [ "tName2ID" ] ); + assert.t( "Find elements that have similar IDs", "#tName2ID", [ "tName2ID" ] ); + + assert.t( "Case-sensitivity", "[name=tname1]", [] ); } ); -QUnit.test( "selectors with comma", function( assert ) { +QUnit.test( "comma-separated", function( assert ) { assert.expect( 4 ); var fixture = jQuery( "

      " ); @@ -101,32 +410,88 @@ QUnit.test( "selectors with comma", function( assert ) { } ); QUnit.test( "child and adjacent", function( assert ) { - assert.expect( 27 ); + assert.expect( 43 ); + + var siblingFirst, en, nothiddendiv; assert.t( "Child", "p > a", [ "simon1", "google", "groups", "mark", "yahoo", "simon" ] ); - assert.t( "Child", "p> a", [ "simon1", "google", "groups", "mark", "yahoo", "simon" ] ); - assert.t( "Child", "p >a", [ "simon1", "google", "groups", "mark", "yahoo", "simon" ] ); - assert.t( "Child", "p>a", [ "simon1", "google", "groups", "mark", "yahoo", "simon" ] ); + assert.t( "Child minus leading whitespace", "p> a", [ "simon1", "google", "groups", "mark", "yahoo", "simon" ] ); + assert.t( "Child minus trailing whitespace", "p >a", [ "simon1", "google", "groups", "mark", "yahoo", "simon" ] ); + assert.t( "Child minus whitespace", "p>a", [ "simon1", "google", "groups", "mark", "yahoo", "simon" ] ); assert.t( "Child w/ Class", "p > a.blog", [ "mark", "simon" ] ); assert.t( "All Children", "code > *", [ "anchor1", "anchor2" ] ); assert.selectInFixture( "All Grandchildren", "p > * > *", [ "anchor1", "anchor2" ] ); - assert.t( "Adjacent", "p + p", [ "ap", "en", "sap" ] ); - assert.t( "Adjacent", "p#firstp + p", [ "ap" ] ); - assert.t( "Adjacent", "p[lang=en] + p", [ "sap" ] ); - assert.t( "Adjacent", "a.GROUPS + code + a", [ "mark" ] ); + + assert.t( "Rooted tag adjacent", "#qunit-fixture a + a", [ "groups", "tName2ID" ] ); + assert.t( "Rooted tag adjacent minus whitespace", "#qunit-fixture a+a", [ "groups", "tName2ID" ] ); + assert.t( "Rooted tag adjacent minus leading whitespace", "#qunit-fixture a +a", + [ "groups", "tName2ID" ] ); + assert.t( "Rooted tag adjacent minus trailing whitespace", "#qunit-fixture a+ a", + [ "groups", "tName2ID" ] ); + + assert.t( "Tag adjacent", "p + p", [ "ap", "en", "sap" ] ); + assert.t( "#id adjacent", "#firstp + p", [ "ap" ] ); + assert.t( "Tag#id adjacent", "p#firstp + p", [ "ap" ] ); + assert.t( "Tag[attr] adjacent", "p[lang=en] + p", [ "sap" ] ); + assert.t( "Tag.class adjacent", "a.GROUPS + code + a", [ "mark" ] ); + assert.t( "Comma, Child, and Adjacent", "#qunit-fixture a + a, code > a", + [ "groups", "anchor1", "anchor2", "tName2ID" ] ); + + assert.t( "Element Preceded By", "#qunit-fixture p ~ div", + [ "foo", "nothiddendiv", "moretests", "tabindex-tests", "liveHandlerOrder", "siblingTest", "fx-test-group" ] ); + assert.t( "Element Preceded By", "#first ~ div", + [ "moretests", "tabindex-tests", "liveHandlerOrder", "siblingTest", "fx-test-group" ] ); assert.t( "Element Preceded By", "#groups ~ a", [ "mark" ] ); assert.t( "Element Preceded By", "#length ~ input", [ "idTest" ] ); assert.t( "Element Preceded By", "#siblingfirst ~ em", [ "siblingnext", "siblingthird" ] ); assert.t( "Element Preceded By (multiple)", "#siblingTest em ~ em ~ em ~ span", [ "siblingspan" ] ); + siblingFirst = document.getElementById( "siblingfirst" ); + + if ( jQuery.find.compile ) { + assert.deepEqual( jQuery.find( "+ em", siblingFirst ), q( "siblingnext" ), + "Element Directly Preceded By with a context." ); + assert.deepEqual( jQuery.find( "~ em", siblingFirst ), q( "siblingnext", "siblingthird" ), + "Element Preceded By with a context." ); + assert.deepEqual( jQuery.find( "~ em:first", siblingFirst ), q( "siblingnext" ), + "Element Preceded By positional with a context." ); + } else { + assert.ok( "skip", "leading + not supported in selector-native" ); + assert.ok( "skip", "leading ~ not supported in selector-native" ); + assert.ok( "skip", "leading ~ not supported in selector-native" ); + } + + if ( jQuery.find.compile ) { + en = document.getElementById( "en" ); + assert.deepEqual( jQuery.find( "+ p, a", en ), q( "yahoo", "sap" ), + "Compound selector with context, beginning with sibling test." ); + assert.deepEqual( jQuery.find( "a, + p", en ), q( "yahoo", "sap" ), + "Compound selector with context, containing sibling test." ); + } else { + assert.ok( "skip", "leading + not supported in selector-native" ); + assert.ok( "skip", "leading + not supported in selector-native" ); + } + if ( jQuery.find.compile ) { assert.t( "Element Preceded By, Containing", "#liveHandlerOrder ~ div em:contains('1')", [ "siblingfirst" ] ); assert.t( "Combinators are not skipped when mixing general and specific", "#siblingTest > em:contains('x') + em ~ span", [] ); assert.equal( jQuery( "#listWithTabIndex li:eq(2) ~ li" ).length, 1, "Find by general sibling combinator (#8310)" ); + + nothiddendiv = document.getElementById( "nothiddendiv" ); + assert.deepEqual( jQuery.find( "> :first", nothiddendiv ), q( "nothiddendivchild" ), + "Verify child context positional selector" ); + assert.deepEqual( jQuery.find( "> :eq(0)", nothiddendiv ), q( "nothiddendivchild" ), + "Verify child context positional selector" ); + assert.deepEqual( jQuery.find( "> *:first", nothiddendiv ), q( "nothiddendivchild" ), + "Verify child context positional selector" ); + } else { assert.ok( "skip", ":contains not supported in selector-native" ); assert.ok( "skip", ":contains not supported in selector-native" ); assert.ok( "skip", ":eq not supported in selector-native" ); + assert.ok( "skip", ":first not supported in selector-native" ); + assert.ok( "skip", ":eq not supported in selector-native" ); + assert.ok( "skip", ":first not supported in selector-native" ); } assert.t( "Multiple combinators selects all levels", "#siblingTest em *", [ "siblingchild", "siblinggrandchild", "siblinggreatgrandchild" ] ); @@ -142,78 +507,143 @@ QUnit.test( "child and adjacent", function( assert ) { assert.t( "Non-existent ancestors", ".fototab > .thumbnails > a", [] ); } ); -QUnit.test( "attributes", function( assert ) { - assert.expect( 54 ); +QUnit.test( "attributes - existence", function( assert ) { + assert.expect( 7 ); - var attrbad, div, withScript; + assert.t( "On element", "#qunit-fixture a[title]", [ "google" ] ); + assert.t( "On element (whitespace ignored)", "#qunit-fixture a[ title ]", [ "google" ] ); + assert.t( "On element (case-insensitive)", "#qunit-fixture a[TITLE]", [ "google" ] ); + assert.t( "On any element", "#qunit-fixture *[title]", [ "google" ] ); + assert.t( "On implicit element", "#qunit-fixture [title]", [ "google" ] ); + assert.t( "Boolean", "#select2 option[selected]", [ "option2d" ] ); + assert.t( "For attribute on label", "form label[for]", [ "label-for" ] ); +} ); - assert.t( "Find elements with a tabindex attribute", "[tabindex]", [ "listWithTabIndex", "foodWithNegativeTabIndex", "linkWithTabIndex", "linkWithNegativeTabIndex", "linkWithNoHrefWithTabIndex", "linkWithNoHrefWithNegativeTabIndex" ] ); +QUnit.test( "attributes - equals", function( assert ) { + assert.expect( 20 ); - assert.t( "Attribute Exists", "#qunit-fixture a[title]", [ "google" ] ); - assert.t( "Attribute Exists (case-insensitive)", "#qunit-fixture a[TITLE]", [ "google" ] ); - assert.t( "Attribute Exists", "#qunit-fixture *[title]", [ "google" ] ); - assert.t( "Attribute Exists", "#qunit-fixture [title]", [ "google" ] ); - assert.t( "Attribute Exists", "#qunit-fixture a[ title ]", [ "google" ] ); + var withScript; - assert.t( "Boolean attribute exists", "#select2 option[selected]", [ "option2d" ] ); - assert.t( "Boolean attribute equals", "#select2 option[selected='selected']", [ "option2d" ] ); + assert.t( "Identifier", "#qunit-fixture a[rel=bookmark]", [ "simon1" ] ); + assert.t( "Identifier with underscore", "input[id=types_all]", [ "types_all" ] ); + assert.t( "String", "#qunit-fixture a[rel='bookmark']", [ "simon1" ] ); + assert.t( "String (whitespace ignored)", "#qunit-fixture a[ rel = 'bookmark' ]", [ "simon1" ] ); + assert.t( "Non-identifier string", "#qunit-fixture a[href='http://www.google.com/']", [ "google" ] ); + assert.t( "Empty string", "#select1 option[value='']", [ "option1a" ] ); - assert.t( "Attribute Equals", "#qunit-fixture a[rel='bookmark']", [ "simon1" ] ); - assert.t( "Attribute Equals", "#qunit-fixture a[rel='bookmark']", [ "simon1" ] ); - assert.t( "Attribute Equals", "#qunit-fixture a[rel=bookmark]", [ "simon1" ] ); - assert.t( "Attribute Equals", "#qunit-fixture a[href='http://www.google.com/']", [ "google" ] ); - assert.t( "Attribute Equals", "#qunit-fixture a[ rel = 'bookmark' ]", [ "simon1" ] ); - assert.t( "Attribute Equals Number", "#qunit-fixture option[value='1']", [ "option1b", "option2b", "option3b", "option4b", "option5c" ] ); - assert.t( "Attribute Equals Number", "#qunit-fixture li[tabIndex='-1']", [ "foodWithNegativeTabIndex" ] ); + if ( jQuery.find.compile ) { + assert.t( "Number", + "#qunit-fixture option[value=1]", + [ "option1b", "option2b", "option3b", "option4b", "option5c" ] ); + assert.t( "negative number", + "#qunit-fixture li[tabIndex=-1]", [ "foodWithNegativeTabIndex" ] ); + } else { + assert.ok( true, "Number value not supported in selector-native" ); + assert.ok( true, "Negative number value not supported in selector-native" ); + } - document.getElementById( "anchor2" ).href = "#2"; - assert.t( "href Attribute", "p a[href^='#']", [ "anchor2" ] ); - assert.t( "href Attribute", "p a[href*='#']", [ "simon1", "anchor2" ] ); + assert.t( "Non-ASCII identifier", "span[lang=中文]", [ "台北" ] ); - assert.t( "for Attribute", "form label[for]", [ "label-for" ] ); + assert.t( "input[type=text]", "#form input[type=text]", [ "text1", "text2", "hidden2", "name" ] ); + assert.t( "input[type=search]", "#form input[type=search]", [ "search" ] ); + + withScript = supportjQuery( "