context (no results)" );
context.removeAttribute( "id" );
assert.deepEqual( jQuery( "*", context ).get(), q( "nothiddendivchild" ), "no-id element context" );
if ( QUnit.jQuerySelectors ) {
assert.deepEqual( jQuery( "* > *", context ).get(), [], "no-id element context (no results)" );
} else {
assert.ok( "skip", ":contains not supported in selector-native" );
}
assert.strictEqual( context.getAttribute( "id" ) || "", "", "id not added by no-id selection" );
context = document.getElementById( "lengthtest" );
assert.deepEqual( jQuery( "input", context ).get(), q( "length", "idTest" ), "
" );
iframeDoc.close();
} );
QUnit.test( "jQuery.escapeSelector", function( assert ) {
assert.expect( 58 );
// Edge cases
assert.equal( jQuery.escapeSelector(), "undefined", "Converts undefined to string" );
assert.equal( jQuery.escapeSelector( "-" ), "\\-", "Escapes standalone dash" );
assert.equal( jQuery.escapeSelector( "-a" ), "-a", "Doesn't escape leading dash followed by non-number" );
assert.equal( jQuery.escapeSelector( "--" ), "--", "Doesn't escape standalone double dash" );
assert.equal( jQuery.escapeSelector( "\uFFFD" ), "\uFFFD",
"Doesn't escape standalone replacement character" );
assert.equal( jQuery.escapeSelector( "a\uFFFD" ), "a\uFFFD",
"Doesn't escape trailing replacement character" );
assert.equal( jQuery.escapeSelector( "\uFFFDb" ), "\uFFFDb",
"Doesn't escape leading replacement character" );
assert.equal( jQuery.escapeSelector( "a\uFFFDb" ), "a\uFFFDb",
"Doesn't escape embedded replacement character" );
// Derived from CSSOM tests
// https://test.csswg.org/harness/test/cssom-1_dev/section/7.1/
// String conversion
assert.equal( jQuery.escapeSelector( true ), "true", "Converts boolean true to string" );
assert.equal( jQuery.escapeSelector( false ), "false", "Converts boolean true to string" );
assert.equal( jQuery.escapeSelector( null ), "null", "Converts null to string" );
assert.equal( jQuery.escapeSelector( "" ), "", "Doesn't modify empty string" );
// Null bytes
assert.equal( jQuery.escapeSelector( "\0" ), "\uFFFD",
"Escapes null-character input as replacement character" );
assert.equal( jQuery.escapeSelector( "a\0" ), "a\uFFFD",
"Escapes trailing-null input as replacement character" );
assert.equal( jQuery.escapeSelector( "\0b" ), "\uFFFDb",
"Escapes leading-null input as replacement character" );
assert.equal( jQuery.escapeSelector( "a\0b" ), "a\uFFFDb",
"Escapes embedded-null input as replacement character" );
// Number prefix
assert.equal( jQuery.escapeSelector( "0a" ), "\\30 a", "Escapes leading 0" );
assert.equal( jQuery.escapeSelector( "1a" ), "\\31 a", "Escapes leading 1" );
assert.equal( jQuery.escapeSelector( "2a" ), "\\32 a", "Escapes leading 2" );
assert.equal( jQuery.escapeSelector( "3a" ), "\\33 a", "Escapes leading 3" );
assert.equal( jQuery.escapeSelector( "4a" ), "\\34 a", "Escapes leading 4" );
assert.equal( jQuery.escapeSelector( "5a" ), "\\35 a", "Escapes leading 5" );
assert.equal( jQuery.escapeSelector( "6a" ), "\\36 a", "Escapes leading 6" );
assert.equal( jQuery.escapeSelector( "7a" ), "\\37 a", "Escapes leading 7" );
assert.equal( jQuery.escapeSelector( "8a" ), "\\38 a", "Escapes leading 8" );
assert.equal( jQuery.escapeSelector( "9a" ), "\\39 a", "Escapes leading 9" );
// Letter-number prefix
assert.equal( jQuery.escapeSelector( "a0b" ), "a0b", "Doesn't escape embedded 0" );
assert.equal( jQuery.escapeSelector( "a1b" ), "a1b", "Doesn't escape embedded 1" );
assert.equal( jQuery.escapeSelector( "a2b" ), "a2b", "Doesn't escape embedded 2" );
assert.equal( jQuery.escapeSelector( "a3b" ), "a3b", "Doesn't escape embedded 3" );
assert.equal( jQuery.escapeSelector( "a4b" ), "a4b", "Doesn't escape embedded 4" );
assert.equal( jQuery.escapeSelector( "a5b" ), "a5b", "Doesn't escape embedded 5" );
assert.equal( jQuery.escapeSelector( "a6b" ), "a6b", "Doesn't escape embedded 6" );
assert.equal( jQuery.escapeSelector( "a7b" ), "a7b", "Doesn't escape embedded 7" );
assert.equal( jQuery.escapeSelector( "a8b" ), "a8b", "Doesn't escape embedded 8" );
assert.equal( jQuery.escapeSelector( "a9b" ), "a9b", "Doesn't escape embedded 9" );
// Dash-number prefix
assert.equal( jQuery.escapeSelector( "-0a" ), "-\\30 a", "Escapes 0 after leading dash" );
assert.equal( jQuery.escapeSelector( "-1a" ), "-\\31 a", "Escapes 1 after leading dash" );
assert.equal( jQuery.escapeSelector( "-2a" ), "-\\32 a", "Escapes 2 after leading dash" );
assert.equal( jQuery.escapeSelector( "-3a" ), "-\\33 a", "Escapes 3 after leading dash" );
assert.equal( jQuery.escapeSelector( "-4a" ), "-\\34 a", "Escapes 4 after leading dash" );
assert.equal( jQuery.escapeSelector( "-5a" ), "-\\35 a", "Escapes 5 after leading dash" );
assert.equal( jQuery.escapeSelector( "-6a" ), "-\\36 a", "Escapes 6 after leading dash" );
assert.equal( jQuery.escapeSelector( "-7a" ), "-\\37 a", "Escapes 7 after leading dash" );
assert.equal( jQuery.escapeSelector( "-8a" ), "-\\38 a", "Escapes 8 after leading dash" );
assert.equal( jQuery.escapeSelector( "-9a" ), "-\\39 a", "Escapes 9 after leading dash" );
// Double dash prefix
assert.equal( jQuery.escapeSelector( "--a" ), "--a", "Doesn't escape leading double dash" );
// Miscellany
assert.equal( jQuery.escapeSelector( "\x01\x02\x1E\x1F" ), "\\1 \\2 \\1e \\1f ",
"Escapes C0 control characters" );
assert.equal( jQuery.escapeSelector( "\x80\x2D\x5F\xA9" ), "\x80\x2D\x5F\xA9",
"Doesn't escape general punctuation or non-ASCII ISO-8859-1 characters" );
assert.equal(
jQuery.escapeSelector( "\x7F\x80\x81\x82\x83\x84\x85\x86\x87\x88\x89\x8A\x8B\x8C\x8D\x8E\x8F\x90" +
"\x91\x92\x93\x94\x95\x96\x97\x98\x99\x9A\x9B\x9C\x9D\x9E\x9F" ),
"\\7f \x80\x81\x82\x83\x84\x85\x86\x87\x88\x89\x8A\x8B\x8C\x8D\x8E\x8F\x90" +
"\x91\x92\x93\x94\x95\x96\x97\x98\x99\x9A\x9B\x9C\x9D\x9E\x9F",
"Escapes DEL control character"
);
assert.equal( jQuery.escapeSelector( "\xA0\xA1\xA2" ), "\xA0\xA1\xA2",
"Doesn't escape non-ASCII ISO-8859-1 characters" );
assert.equal( jQuery.escapeSelector( "a0123456789b" ), "a0123456789b",
"Doesn't escape embedded numbers" );
assert.equal( jQuery.escapeSelector( "abcdefghijklmnopqrstuvwxyz" ), "abcdefghijklmnopqrstuvwxyz",
"Doesn't escape lowercase ASCII letters" );
assert.equal( jQuery.escapeSelector( "ABCDEFGHIJKLMNOPQRSTUVWXYZ" ), "ABCDEFGHIJKLMNOPQRSTUVWXYZ",
"Doesn't escape uppercase ASCII letters" );
assert.equal( jQuery.escapeSelector( "\x20\x21\x78\x79" ), "\\ \\!xy",
"Escapes non-word ASCII characters" );
// Astral symbol (U+1D306 TETRAGRAM FOR CENTRE)
assert.equal( jQuery.escapeSelector( "\uD834\uDF06" ), "\uD834\uDF06",
"Doesn't escape astral characters" );
// Lone surrogates
assert.equal( jQuery.escapeSelector( "\uDF06" ), "\uDF06", "Doesn't escape lone low surrogate" );
assert.equal( jQuery.escapeSelector( "\uD834" ), "\uD834", "Doesn't escape lone high surrogate" );
} );
QUnit[ QUnit.jQuerySelectors ? "test" : "skip" ]( "custom pseudos", function( assert ) {
assert.expect( 6 );
try {
jQuery.expr.filters.foundation = jQuery.expr.filters.root;
assert.deepEqual( jQuery.find( ":foundation" ), [ document.documentElement ], "Copy element filter with new name" );
} finally {
delete jQuery.expr.filters.foundation;
}
try {
jQuery.expr.setFilters.primary = jQuery.expr.setFilters.first;
assert.t( "Copy set filter with new name", "div#qunit-fixture :primary", [ "firstp" ] );
} finally {
delete jQuery.expr.setFilters.primary;
}
try {
jQuery.expr.filters.aristotlean = jQuery.expr.createPseudo( function() {
return function( elem ) {
return !!elem.id;
};
} );
assert.t( "Custom element filter", "#foo :aristotlean", [ "sndp", "en", "yahoo", "sap", "anchor2", "simon" ] );
} finally {
delete jQuery.expr.filters.aristotlean;
}
try {
jQuery.expr.filters.endswith = jQuery.expr.createPseudo( function( text ) {
return function( elem ) {
return jQuery.text( elem ).slice( -text.length ) === text;
};
} );
assert.t( "Custom element filter with argument", "a:endswith(ogle)", [ "google" ] );
} finally {
delete jQuery.expr.filters.endswith;
}
try {
jQuery.expr.setFilters.second = jQuery.expr.createPseudo( function() {
return jQuery.expr.createPseudo( function( seed, matches ) {
if ( seed[ 1 ] ) {
matches[ 1 ] = seed[ 1 ];
seed[ 1 ] = false;
}
} );
} );
assert.t( "Custom set filter", "#qunit-fixture p:second", [ "ap" ] );
} finally {
delete jQuery.expr.filters.second;
}
try {
jQuery.expr.setFilters.slice = jQuery.expr.createPseudo( function( argument ) {
var bounds = argument.split( ":" );
return jQuery.expr.createPseudo( function( seed, matches ) {
var i = bounds[ 1 ];
// Match elements found at the specified indexes
while ( --i >= bounds[ 0 ] ) {
if ( seed[ i ] ) {
matches[ i ] = seed[ i ];
seed[ i ] = false;
}
}
} );
} );
assert.t( "Custom set filter with argument", "#qunit-fixture p:slice(1:3)", [ "ap", "sndp" ] );
} finally {
delete jQuery.expr.filters.slice;
}
} );
QUnit.test( "jQuery.find.matchesSelector", function( assert ) {
assert.expect( 15 );
var link = document.getElementById( "simon1" ),
input = document.getElementById( "text1" ),
option = document.getElementById( "option1a" ),
disconnected = document.createElement( "div" );
link.title = "Don't click me";
assert.ok( jQuery.find.matchesSelector( link, "[rel='bookmark']" ), "attribute-equals string" );
assert.ok( jQuery.find.matchesSelector( link, "[rel=bookmark]" ), "attribute-equals identifier" );
assert.ok( jQuery.find.matchesSelector( link, "[\nrel = bookmark\t]" ),
"attribute-equals identifier (whitespace ignored)" );
assert.ok( jQuery.find.matchesSelector( link, "a[title=\"Don't click me\"]" ),
"attribute-equals string containing single quote" );
// trac-12303
input.setAttribute( "data-pos", ":first" );
assert.ok( jQuery.find.matchesSelector( input, "input[data-pos=\\:first]" ),
"attribute-equals POS in identifier" );
assert.ok( jQuery.find.matchesSelector( input, "input[data-pos=':first']" ),
"attribute-equals POS in string" );
if ( QUnit.jQuerySelectors ) {
assert.ok( jQuery.find.matchesSelector( input, ":input[data-pos=':first']" ),
"attribute-equals POS in string after pseudo" );
} else {
assert.ok( "skip", ":input not supported in selector-native" );
}
option.setAttribute( "test", "" );
assert.ok( jQuery.find.matchesSelector( option, "[id=option1a]" ),
"id attribute-equals identifier" );
if ( QUnit.jQuerySelectors ) {
assert.ok( jQuery.find.matchesSelector( option, "[id*=option1][type!=checkbox]" ),
"attribute-not-equals identifier" );
} else {
assert.ok( "skip", "[key!=value] not supported in selector-native" );
}
assert.ok( jQuery.find.matchesSelector( option, "[id*=option1]" ), "attribute-contains identifier" );
assert.ok( !jQuery.find.matchesSelector( option, "[test^='']" ),
"attribute-starts-with empty string (negative)" );
option.className = "=]";
assert.ok( jQuery.find.matchesSelector( option, ".\\=\\]" ),
"class selector with attribute-equals confusable" );
assert.ok( jQuery.find.matchesSelector( disconnected, "div" ), "disconnected element" );
assert.ok( jQuery.find.matchesSelector( link, "* > *" ), "child combinator matches in document" );
assert.ok( !jQuery.find.matchesSelector( disconnected, "* > *" ), "child combinator fails in fragment" );
} );
QUnit.test( "jQuery.find.matches", function( assert ) {
assert.expect( 4 );
var iframeChild,
input = document.getElementById( "text1" ),
div = document.createElement( "div" ),
iframe = document.getElementById( "iframe" ),
iframeDoc = iframe.contentDocument || iframe.contentWindow.document;
assert.deepEqual( jQuery.find.matches( "input", [ input ] ), [ input ],
"jQuery.find.matches with seed of input element" );
assert.deepEqual( jQuery.find.matches( "div", [ div ] ), [ div ],
"jQuery.find.matches with disconnected element" );
iframeDoc.open();
iframeDoc.write( "
" );
iframeDoc.close();
iframeChild = iframeDoc.getElementById( "bar" );
assert.deepEqual(
jQuery.find.matches( ":root > body > #foo > #bar", [ iframeChild ] ),
[ iframeChild ],
"jQuery.find.matches infers context from element"
);
assert.deepEqual(
jQuery.find.matches( ":root *", [ div, iframeChild, input ] ),
[ iframeChild, input ],
"jQuery.find.matches infers context from each seed element"
);
} );
QUnit[ QUnit.jQuerySelectors ? "test" : "skip" ]( "jQuery.find.select with pre-compiled function", function( assert ) {
assert.expect( 6 );
supportjQuery.each( [
"#qunit-fixture #first",
"ol#listWithTabIndex > li[tabindex]",
"#liveSpan1"
], function( _i, selector ) {
var compiled = jQuery.find.compile( selector );
assert.equal( jQuery.find.select( compiled, document ).length,
1, "Should match using a compiled selector function" );
assert.equal(
jQuery.find.select( compiled, jQuery( "#first" )[ 0 ] ).length,
0, "Should not match with different context" );
} );
} );
// Internal, but we test it for backwards compatibility for edge cases
QUnit[ QUnit.jQuerySelectors ? "test" : "skip" ]( "jQuery.find.tokenize", function( assert ) {
assert.expect( 1 );
var selector = "#id .class > div[prop=\"value\"] + input:nth-child(1):button, span:contains(\"Text\") ~ div:has(div:has(span)):not(.not-this.not-that > div)",
tokens = [
[
{
"value": "#id",
"type": "ID",
"matches": [
"id"
]
},
{
"value": " ",
"type": " "
},
{
"value": ".class",
"type": "CLASS",
"matches": [
"class"
]
},
{
"value": " > ",
"type": ">"
},
{
"value": "div",
"type": "TAG",
"matches": [
"div"
]
},
{
"value": "[prop=\"value\"]",
"type": "ATTR",
"matches": [
"prop",
"=",
"value"
]
},
{
"value": " + ",
"type": "+"
},
{
"value": "input",
"type": "TAG",
"matches": [
"input"
]
},
{
"value": ":nth-child(1)",
"type": "CHILD",
"matches": [
"nth",
"child",
"1",
0,
1,
undefined,
"",
"1"
]
},
{
"value": ":button",
"type": "PSEUDO",
"matches": [
"button",
undefined
]
}
],
[
{
"value": "span",
"type": "TAG",
"matches": [
"span"
]
},
{
"value": ":contains(\"Text\")",
"type": "PSEUDO",
"matches": [
"contains",
"Text"
]
},
{
"value": " ~ ",
"type": "~"
},
{
"value": "div",
"type": "TAG",
"matches": [
"div"
]
},
{
"value": ":has(div:has(span))",
"type": "PSEUDO",
"matches": [
"has",
"div:has(span)"
]
},
{
"value": ":not(.not-this.not-that > div)",
"type": "PSEUDO",
"matches": [
"not",
".not-this.not-that > div"
]
}
]
];
assert.deepEqual( jQuery.find.tokenize( selector ), tokens, "Tokenization successful" );
} );