diff --git a/.editorconfig b/.editorconfig index b5bd7f60ec..0fe2cbbba5 100644 --- a/.editorconfig +++ b/.editorconfig @@ -10,7 +10,6 @@ charset = utf-8 trim_trailing_whitespace = true insert_final_newline = true -[package.json] +[*.{json,yml}] indent_style = space indent_size = 2 - diff --git a/.eslintignore b/.eslintignore deleted file mode 100644 index e1d456ddfd..0000000000 --- a/.eslintignore +++ /dev/null @@ -1,16 +0,0 @@ -amd -external -node_modules -*.min.js -dist/** -!dist/jquery.js -!dist/jquery.min.js -test/data/jquery-1.9.1.js -test/data/badcall.js -test/data/badjson.js -test/data/json_obj.js -test/data/readywaitasset.js -test/data/readywaitloader.js -test/data/support/csp.js -test/data/support/getComputedSupport.js -test/data/core/jquery-iterability-transpiled.js diff --git a/.eslintrc-browser.json b/.eslintrc-browser.json deleted file mode 100644 index 97b5abe852..0000000000 --- a/.eslintrc-browser.json +++ /dev/null @@ -1,27 +0,0 @@ -{ - "root": true, - - "extends": "jquery", - - "reportUnusedDisableDirectives": true, - - "parserOptions": { - "ecmaVersion": 5 - }, - - // The browser env is not enabled on purpose so that code takes - // all browser-only globals from window instead of assuming - // they're available as globals. This makes it possible to use - // jQuery with tools like jsdom which provide a custom window - // implementation. - "env": {}, - - "globals": { - "window": true - }, - - "rules": { - "one-var": ["error", {"var": "always"}], - "strict": ["error", "function"] - } -} diff --git a/.eslintrc-node.json b/.eslintrc-node.json deleted file mode 100644 index 8cb69807b3..0000000000 --- a/.eslintrc-node.json +++ /dev/null @@ -1,20 +0,0 @@ -{ - "root": true, - - "extends": "jquery", - - "reportUnusedDisableDirectives": true, - - "parserOptions": { - "ecmaVersion": 2018 - }, - - "env": { - "es6": true, - "node": true - }, - - "rules": { - "strict": ["error", "global"] - } -} diff --git a/.eslintrc.json b/.eslintrc.json deleted file mode 100644 index d2c977ca85..0000000000 --- a/.eslintrc.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "root": true, - - "extends": "./.eslintrc-node.json" -} diff --git a/.gitattributes b/.gitattributes index b7ca95b5b7..5832a0194d 100644 --- a/.gitattributes +++ b/.gitattributes @@ -2,4 +2,7 @@ * text=auto # JS files must always use LF for tools to work +# JS files may have mjs or cjs extensions now as well *.js eol=lf +*.cjs eol=lf +*.mjs eol=lf diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md index 828672208e..ec0910b10e 100644 --- a/.github/PULL_REQUEST_TEMPLATE.md +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -11,7 +11,6 @@ Mark an `[x]` for completed items, if you're not sure leave them unchecked and w --> * [ ] New tests have been added to show the fix or feature works -* [ ] Grunt build and unit tests pass locally with these changes * [ ] If needed, a docs issue/PR was created at https://github.com/jquery/api.jquery.com - + @@ -19,27 +19,28 @@ - + - + diff --git a/test/jquery.js b/test/jquery.js index 3292852510..cd9c490b10 100644 --- a/test/jquery.js +++ b/test/jquery.js @@ -1,15 +1,8 @@ // Use the right jQuery source on the test page (and iframes) ( function() { - /* global loadTests: false */ - var dynamicImportSource, config, src, - FILEPATH = "/test/jquery.js", - activeScript = [].slice.call( document.getElementsByTagName( "script" ), -1 )[ 0 ], - parentUrl = activeScript && activeScript.src ? - activeScript.src.replace( /[?#].*/, "" ) + FILEPATH.replace( /[^/]+/g, ".." ) + "/" : - "../", - QUnit = window.QUnit, - require = window.require; + parentUrl = window.location.protocol + "//" + window.location.host, + QUnit = window.QUnit; function getQUnitConfig() { var config = Object.create( null ); @@ -37,10 +30,6 @@ id: "esmodules", label: "Load as modules", tooltip: "Load the jQuery module file (and its dependencies)" - }, { - id: "amd", - label: "Load with AMD", - tooltip: "Load the AMD jQuery file (and its dependencies)" }, { id: "dev", label: "Load unminified", @@ -62,8 +51,8 @@ // IE doesn't support the dynamic import syntax so it would crash // with a SyntaxError here. dynamicImportSource = "" + - "import( `${ parentUrl }src/jquery.js` )\n" + - " .then( ( { default: jQuery } ) => {\n" + + "import( `${ parentUrl }/src/jquery.js` )\n" + + " .then( ( { jQuery } ) => {\n" + " window.jQuery = jQuery;\n" + " if ( typeof loadTests === \"function\" ) {\n" + " // Include tests if specified\n" + @@ -77,23 +66,9 @@ eval( dynamicImportSource ); - // Apply similar treatment for AMD modules - } else if ( config.amd && QUnit ) { - require.config( { - baseUrl: parentUrl - } ); - src = "amd/jquery"; - - // Include tests if specified - if ( typeof loadTests !== "undefined" ) { - require( [ src ], loadTests ); - } else { - require( [ src ] ); - } - // Otherwise, load synchronously } else { - document.write( " - - %SCRIPTS% - - - - - - diff --git a/test/karma.debug.html b/test/karma.debug.html deleted file mode 100644 index 9eb6c9885e..0000000000 --- a/test/karma.debug.html +++ /dev/null @@ -1,46 +0,0 @@ - - - -%X_UA_COMPATIBLE% - DEBUG - - - - - - -
- - - - -
- - - - - - - %SCRIPTS% - - - - - - diff --git a/test/localfile.html b/test/localfile.html deleted file mode 100644 index 5a79bfec17..0000000000 --- a/test/localfile.html +++ /dev/null @@ -1,75 +0,0 @@ - - - - - jQuery Local File Test - - - - - -

jQuery Local File Test

-

- Introduction -

- -

- Results -

- -

- Logs: -

- - - diff --git a/test/middleware-mockserver.js b/test/middleware-mockserver.cjs similarity index 60% rename from test/middleware-mockserver.js rename to test/middleware-mockserver.cjs index ea6a5d2b0d..a07cb47984 100644 --- a/test/middleware-mockserver.js +++ b/test/middleware-mockserver.cjs @@ -1,9 +1,25 @@ -/* eslint-env node */ -var url = require( "url" ); -var fs = require( "fs" ); -var getRawBody = require( "raw-body" ); +"use strict"; + +const url = require( "node:url" ); +const fs = require( "node:fs" ); +const getRawBody = require( "raw-body" ); +const multiparty = require( "multiparty" ); + +let cspLog = ""; + +/** + * Like `readFileSync`, but on error returns "ERROR" + * without crashing. + * @param path + */ +function readFileSync( path ) { + try { + return fs.readFileSync( path ); + } catch ( e ) { + return "ERROR"; + } +} -var cspLog = ""; /** * Keep in sync with /test/mock.php */ @@ -11,7 +27,7 @@ function cleanCallback( callback ) { return callback.replace( /[^a-z0-9_]/gi, "" ); } -var mocks = { +const mocks = { contentType: function( req, resp ) { resp.writeHead( 200, { "content-type": req.query.contentType @@ -19,7 +35,7 @@ var mocks = { resp.end( req.query.response ); }, wait: function( req, resp ) { - var wait = Number( req.query.wait ) * 1000; + const wait = Number( req.query.wait ) * 1000; setTimeout( function() { if ( req.query.script ) { resp.writeHead( 200, { "content-type": "text/javascript" } ); @@ -44,7 +60,7 @@ var mocks = { }, next ); }, xml: function( req, resp, next ) { - var content = "5-23"; + const content = "5-23"; resp.writeHead( 200, { "content-type": "text/xml" } ); if ( req.query.cal === "5-2" ) { @@ -59,32 +75,35 @@ var mocks = { } }, next ); }, - atom: function( req, resp, next ) { + atom: function( _req, resp ) { resp.writeHead( 200, { "content-type": "atom+xml" } ); resp.end( "" ); }, script: function( req, resp ) { + const headers = {}; if ( req.query.header === "ecma" ) { - resp.writeHead( 200, { "content-type": "application/ecmascript" } ); + headers[ "content-type" ] = "application/ecmascript"; } else if ( "header" in req.query ) { - resp.writeHead( 200, { "content-type": "text/javascript" } ); + headers[ "content-type" ] = "text/javascript"; } else { - resp.writeHead( 200, { "content-type": "text/html" } ); + headers[ "content-type" ] = "text/html"; } if ( req.query.cors ) { - resp.writeHead( 200, { "access-control-allow-origin": "*" } ); + headers[ "access-control-allow-origin" ] = "*"; } + resp.writeHead( 200, headers ); + if ( req.query.callback ) { - resp.end( cleanCallback( req.query.callback ) + "(" + JSON.stringify( { + resp.end( `${ cleanCallback( req.query.callback ) }(${ JSON.stringify( { headers: req.headers - } ) + ")" ); + } ) })` ); } else { resp.end( "QUnit.assert.ok( true, \"mock executed\" );" ); } }, - testbar: function( req, resp ) { + testbar: function( _req, resp ) { resp.writeHead( 200 ); resp.end( "this.testBar = 'bar'; " + @@ -93,12 +112,14 @@ var mocks = { ); }, json: function( req, resp ) { + const headers = {}; if ( req.query.header ) { - resp.writeHead( 200, { "content-type": "application/json" } ); + headers[ "content-type" ] = "application/json"; } if ( req.query.cors ) { - resp.writeHead( 200, { "access-control-allow-origin": "*" } ); + headers[ "access-control-allow-origin" ] = "*"; } + resp.writeHead( 200, headers ); if ( req.query.array ) { resp.end( JSON.stringify( [ { name: "John", age: 21 }, { name: "Peter", age: 25 } ] @@ -110,7 +131,7 @@ var mocks = { } }, jsonp: function( req, resp, next ) { - var callback; + let callback; if ( Array.isArray( req.query.callback ) ) { callback = Promise.resolve( req.query.callback[ req.query.callback.length - 1 ] ); } else if ( req.query.callback ) { @@ -122,7 +143,7 @@ var mocks = { return body.trim().replace( "callback=", "" ); } ); } - var json = req.query.array ? + const json = req.query.array ? JSON.stringify( [ { name: "John", age: 21 }, { name: "Peter", age: 25 } ] ) : @@ -130,14 +151,27 @@ var mocks = { { data: { lang: "en", length: 25 } } ); callback.then( function( cb ) { - resp.end( cleanCallback( cb ) + "(" + json + ")" ); + resp.end( `${ cleanCallback( cb ) }(${ json })` ); }, next ); }, xmlOverJsonp: function( req, resp ) { - var callback = req.query.callback; - var body = fs.readFileSync( __dirname + "/data/with_fries.xml" ).toString(); + const callback = req.query.callback; + const body = readFileSync( `${ __dirname }/data/with_fries.xml` ).toString(); + resp.writeHead( 200 ); + resp.end( `${ cleanCallback( callback ) }(${ JSON.stringify( body ) })\n` ); + }, + formData: function( req, resp, next ) { + const prefix = "multipart/form-data; boundary=--"; + const contentTypeValue = req.headers[ "content-type" ]; resp.writeHead( 200 ); - resp.end( cleanCallback( callback ) + "(" + JSON.stringify( body ) + ")\n" ); + if ( ( prefix || "" ).startsWith( prefix ) ) { + getMultiPartContent( req ).then( function( { fields = {} } ) { + resp.end( `key1 -> ${ fields.key1 }, key2 -> ${ fields.key2 }` ); + }, next ); + } else { + resp.end( `Incorrect Content-Type: ${ contentTypeValue + }\nExpected prefix: ${ prefix }` ); + } }, error: function( req, resp ) { if ( req.query.json ) { @@ -149,17 +183,19 @@ var mocks = { } }, headers: function( req, resp ) { - resp.writeHead( 200, { + const headers = { "Sample-Header": "Hello World", "Empty-Header": "", "Sample-Header2": "Hello World 2", "List-Header": "Item 1", "list-header": "Item 2", "constructor": "prototype collision (constructor)" - } ); + }; + + resp.writeHead( 200, headers ); req.query.keys.split( "|" ).forEach( function( key ) { if ( key.toLowerCase() in req.headers ) { - resp.write( key + ": " + req.headers[ key.toLowerCase() ] + "\n" ); + resp.write( `${ key }: ${ req.headers[ key.toLowerCase() ] }\n` ); } } ); resp.end(); @@ -177,16 +213,16 @@ var mocks = { }, echoHtml: function( req, resp, next ) { resp.writeHead( 200, { "Content-Type": "text/html" } ); - resp.write( "
" + req.method + "
" ); - resp.write( "
" + req.parsed.search.slice( 1 ) + "
" ); + resp.write( `
${ req.method }
` ); + resp.write( `
${ req.parsed.search.slice( 1 ) }
` ); getBody( req ).then( function( body ) { - resp.write( "
" + body + "
" ); + resp.write( `
${ body }
` ); resp.end( body ); }, next ); }, etag: function( req, resp ) { - var hash = Number( req.query.ts ).toString( 36 ); - var etag = "W/\"" + hash + "\""; + const hash = Number( req.query.ts ).toString( 36 ); + const etag = `W/"${ hash }"`; if ( req.headers[ "if-none-match" ] === etag ) { resp.writeHead( 304 ); resp.end(); @@ -197,8 +233,8 @@ var mocks = { } ); resp.end(); }, - ims: function( req, resp, next ) { - var ts = req.query.ts; + ims: function( req, resp ) { + const ts = req.query.ts; if ( req.headers[ "if-modified-since" ] === ts ) { resp.writeHead( 304 ); resp.end(); @@ -209,67 +245,78 @@ var mocks = { } ); resp.end(); }, - status: function( req, resp, next ) { + status: function( req, resp ) { resp.writeHead( Number( req.query.code ) ); resp.end(); }, testHTML: function( req, resp ) { resp.writeHead( 200, { "Content-Type": "text/html" } ); - var body = fs.readFileSync( __dirname + "/data/test.include.html" ).toString(); - body = body.replace( /{{baseURL}}/g, req.query.baseURL ); + const body = readFileSync( + `${ __dirname }/data/test.include.html` + ) + .toString() + .replace( /{{baseURL}}/g, req.query.baseURL ); resp.end( body ); }, - cspFrame: function( req, resp ) { + cspFrame: function( _req, resp ) { resp.writeHead( 200, { "Content-Type": "text/html", - "Content-Security-Policy": "default-src 'self'; require-trusted-types-for 'script'; report-uri /base/test/data/mock.php?action=cspLog" + "Content-Security-Policy": "default-src 'self'; require-trusted-types-for 'script'; " + + "report-uri /test/data/mock.php?action=cspLog" } ); - var body = fs.readFileSync( __dirname + "/data/csp.include.html" ).toString(); + const body = readFileSync( `${ __dirname }/data/csp.include.html` ).toString(); resp.end( body ); }, cspNonce: function( req, resp ) { - var testParam = req.query.test ? "-" + req.query.test : ""; + const testParam = req.query.test ? + `-${ req.query.test.replace( /[^a-z0-9]/gi, "" ) }` : + ""; resp.writeHead( 200, { "Content-Type": "text/html", - "Content-Security-Policy": "script-src 'nonce-jquery+hardcoded+nonce'; report-uri /base/test/data/mock.php?action=cspLog" + "Content-Security-Policy": "script-src 'nonce-jquery+hardcoded+nonce'; " + + "report-uri /test/data/mock.php?action=cspLog" } ); - var body = fs.readFileSync( - __dirname + "/data/csp-nonce" + testParam + ".html" ).toString(); + const body = readFileSync( + `${ __dirname }/data/csp-nonce${ testParam }.html` ).toString(); resp.end( body ); }, - cspAjaxScript: function( req, resp ) { + cspAjaxScript: function( _req, resp ) { resp.writeHead( 200, { "Content-Type": "text/html", - "Content-Security-Policy": "script-src 'self'; report-uri /base/test/data/mock.php?action=cspLog" + "Content-Security-Policy": "script-src 'self'; " + + "report-uri /test/data/mock.php?action=cspLog" } ); - var body = fs.readFileSync( - __dirname + "/data/csp-ajax-script.html" ).toString(); + const body = readFileSync( + `${ __dirname }/data/csp-ajax-script.html` ).toString(); resp.end( body ); }, - cspLog: function( req, resp ) { + cspLog: function( _req, resp ) { cspLog = "error"; resp.writeHead( 200 ); resp.end(); }, - cspClean: function( req, resp ) { + cspClean: function( _req, resp ) { cspLog = ""; resp.writeHead( 200 ); resp.end(); }, - trustedHtml: function( req, resp ) { + trustedHtml: function( _req, resp ) { resp.writeHead( 200, { "Content-Type": "text/html", - "Content-Security-Policy": "require-trusted-types-for 'script'; report-uri /base/test/data/mock.php?action=cspLog" + "Content-Security-Policy": "require-trusted-types-for 'script'; " + + "report-uri /test/data/mock.php?action=cspLog" } ); - var body = fs.readFileSync( __dirname + "/data/trusted-html.html" ).toString(); + const body = readFileSync( `${ __dirname }/data/trusted-html.html` ).toString(); resp.end( body ); }, - trustedTypesAttributes: function( req, resp ) { + trustedTypesAttributes: function( _req, resp ) { resp.writeHead( 200, { "Content-Type": "text/html", - "Content-Security-Policy": "require-trusted-types-for 'script'; report-uri /base/test/data/mock.php?action=cspLog" + "Content-Security-Policy": "require-trusted-types-for 'script'; " + + "report-uri /test/data/mock.php?action=cspLog" } ); - var body = fs.readFileSync( __dirname + "/data/trusted-types-attributes.html" ).toString(); + const body = readFileSync( + `${ __dirname }/data/trusted-types-attributes.html` ).toString(); resp.end( body ); }, errorWithScript: function( req, resp ) { @@ -279,14 +326,14 @@ var mocks = { resp.writeHead( 404, { "Content-Type": "text/html; charset=UTF-8" } ); } if ( req.query.callback ) { - resp.end( cleanCallback( req.query.callback ) + - "( {\"status\": 404, \"msg\": \"Not Found\"} )" ); + resp.end( `${ cleanCallback( req.query.callback ) + }( {"status": 404, "msg": "Not Found"} )` ); } else { resp.end( "QUnit.assert.ok( false, \"Mock return erroneously executed\" );" ); } } }; -var handlers = { +const handlers = { "test/data/mock.php": function( req, resp, next ) { if ( !mocks[ req.query.action ] ) { resp.writeHead( 400 ); @@ -296,11 +343,11 @@ var handlers = { } mocks[ req.query.action ]( req, resp, next ); }, - "test/data/support/csp.log": function( req, resp ) { + "test/data/support/csp.log": function( _req, resp ) { resp.writeHead( 200 ); resp.end( cspLog ); }, - "test/data/404.txt": function( req, resp ) { + "test/data/404.txt": function( _req, resp ) { resp.writeHead( 404 ); resp.end( "" ); } @@ -308,28 +355,26 @@ var handlers = { /** * Connect-compatible middleware factory for mocking server responses. - * Used by Ajax unit tests when run via Karma. - * - * Despite Karma using Express, it uses Connect to deal with custom middleware, - * which passes the raw Node Request and Response objects instead of the - * Express versions of these (e.g. no req.path, req.query, resp.set). + * Used by Ajax tests run in Node. */ function MockserverMiddlewareFactory() { + /** * @param {http.IncomingMessage} req * @param {http.ServerResponse} resp * @param {Function} next Continue request handling */ return function( req, resp, next ) { - var parsed = url.parse( req.url, /* parseQuery */ true ), - path = parsed.pathname.replace( /^\/base\//, "" ), - query = parsed.query, - subReq = Object.assign( Object.create( req ), { - query: query, - parsed: parsed - } ); + const parsed = url.parse( req.url, /* parseQuery */ true ); + let path = parsed.pathname; + const query = parsed.query; + const subReq = Object.assign( Object.create( req ), { + query: query, + parsed: parsed + } ); + + if ( /^\/?test\/data\/mock.php\/?/.test( path ) ) { - if ( /^test\/data\/mock.php\//.test( path ) ) { // Support REST-like Apache PathInfo path = "test\/data\/mock.php"; } @@ -339,6 +384,7 @@ function MockserverMiddlewareFactory() { return; } + // console.log( "Mock handling", req.method, parsed.href ); handlers[ path ]( subReq, resp, next ); }; } @@ -351,4 +397,18 @@ function getBody( req ) { } ); } +function getMultiPartContent( req ) { + return new Promise( function( resolve ) { + if ( req.method !== "POST" ) { + resolve( "" ); + return; + } + + const form = new multiparty.Form(); + form.parse( req, function( _err, fields, files ) { + resolve( { fields, files } ); + } ); + } ); +} + module.exports = MockserverMiddlewareFactory; diff --git a/test/node_smoke_tests/.eslintrc.json b/test/node_smoke_tests/.eslintrc.json deleted file mode 100644 index a1bd6ec3f4..0000000000 --- a/test/node_smoke_tests/.eslintrc.json +++ /dev/null @@ -1,13 +0,0 @@ -{ - "root": true, - - "extends": "../../.eslintrc-node.json", - - "parserOptions": { - "ecmaVersion": 6 - }, - - "env": { - "es6": true - } -} diff --git a/test/node_smoke_tests/commonjs/factory/document_missing.cjs b/test/node_smoke_tests/commonjs/factory/document_missing.cjs new file mode 100644 index 0000000000..2587c9989a --- /dev/null +++ b/test/node_smoke_tests/commonjs/factory/document_missing.cjs @@ -0,0 +1,15 @@ +"use strict"; + +const assert = require( "node:assert/strict" ); + +const { ensureGlobalNotCreated } = require( "../lib/ensure_global_not_created.cjs" ); +const { getJQueryModuleSpecifier } = require( "../lib/jquery-module-specifier.cjs" ); + +const jQueryModuleSpecifier = getJQueryModuleSpecifier(); +const { jQueryFactory } = require( jQueryModuleSpecifier ); + +assert.throws( () => { + jQueryFactory( {} ); +}, /jQuery requires a window with a document/ ); + +ensureGlobalNotCreated( module.exports ); diff --git a/test/node_smoke_tests/commonjs/factory/document_passed.cjs b/test/node_smoke_tests/commonjs/factory/document_passed.cjs new file mode 100644 index 0000000000..d7770b00a1 --- /dev/null +++ b/test/node_smoke_tests/commonjs/factory/document_passed.cjs @@ -0,0 +1,16 @@ +"use strict"; + +const { JSDOM } = require( "jsdom" ); + +const { ensureJQuery } = require( "../lib/ensure_jquery.cjs" ); +const { ensureGlobalNotCreated } = require( "../lib/ensure_global_not_created.cjs" ); +const { getJQueryModuleSpecifier } = require( "../lib/jquery-module-specifier.cjs" ); + +const { window } = new JSDOM( "" ); + +const jQueryModuleSpecifier = getJQueryModuleSpecifier(); +const { jQueryFactory } = require( jQueryModuleSpecifier ); +const jQuery = jQueryFactory( window ); + +ensureJQuery( jQuery ); +ensureGlobalNotCreated( module.exports ); diff --git a/test/node_smoke_tests/commonjs/factory/iterable_with_native_symbol.cjs b/test/node_smoke_tests/commonjs/factory/iterable_with_native_symbol.cjs new file mode 100644 index 0000000000..d973271b43 --- /dev/null +++ b/test/node_smoke_tests/commonjs/factory/iterable_with_native_symbol.cjs @@ -0,0 +1,14 @@ +"use strict"; + +const process = require( "node:process" ); + +if ( typeof Symbol === "undefined" ) { + console.log( "Symbols not supported, skipping the test..." ); + process.exit(); +} + +const { ensureIterability } = require( "../lib/ensure_iterability_es6.cjs" ); +const { getJQueryModuleSpecifier } = require( "../lib/jquery-module-specifier.cjs" ); + +const jQueryModuleSpecifier = getJQueryModuleSpecifier(); +ensureIterability( jQueryModuleSpecifier ); diff --git a/test/node_smoke_tests/lib/ensure_global_not_created.js b/test/node_smoke_tests/commonjs/lib/ensure_global_not_created.cjs similarity index 81% rename from test/node_smoke_tests/lib/ensure_global_not_created.js rename to test/node_smoke_tests/commonjs/lib/ensure_global_not_created.cjs index 95db622267..de27739ebd 100644 --- a/test/node_smoke_tests/lib/ensure_global_not_created.js +++ b/test/node_smoke_tests/commonjs/lib/ensure_global_not_created.cjs @@ -1,6 +1,6 @@ "use strict"; -const assert = require( "assert" ); +const assert = require( "node:assert/strict" ); // Ensure the jQuery property on global/window/module.exports/etc. was not // created in a CommonJS environment. @@ -12,4 +12,4 @@ const ensureGlobalNotCreated = ( ...args ) => { } ); }; -module.exports = ensureGlobalNotCreated; +module.exports = { ensureGlobalNotCreated }; diff --git a/test/node_smoke_tests/commonjs/lib/ensure_iterability_es6.cjs b/test/node_smoke_tests/commonjs/lib/ensure_iterability_es6.cjs new file mode 100644 index 0000000000..1226970e45 --- /dev/null +++ b/test/node_smoke_tests/commonjs/lib/ensure_iterability_es6.cjs @@ -0,0 +1,25 @@ +"use strict"; + +const assert = require( "node:assert/strict" ); +const { JSDOM } = require( "jsdom" ); + +const { ensureJQuery } = require( "./ensure_jquery.cjs" ); + +const ensureIterability = ( jQueryModuleSpecifier ) => { + const { window } = new JSDOM( "" ); + + const { jQueryFactory } = require( jQueryModuleSpecifier ); + const jQuery = jQueryFactory( window ); + const elem = jQuery( "
" ); + + ensureJQuery( jQuery ); + + let result = ""; + for ( const node of elem ) { + result += node.nodeName; + } + + assert.strictEqual( result, "DIVSPANA", "for-of works on jQuery objects" ); +}; + +module.exports = { ensureIterability }; diff --git a/test/node_smoke_tests/lib/ensure_jquery.js b/test/node_smoke_tests/commonjs/lib/ensure_jquery.cjs similarity index 75% rename from test/node_smoke_tests/lib/ensure_jquery.js rename to test/node_smoke_tests/commonjs/lib/ensure_jquery.cjs index 5b7c064f14..cbca140326 100644 --- a/test/node_smoke_tests/lib/ensure_jquery.js +++ b/test/node_smoke_tests/commonjs/lib/ensure_jquery.cjs @@ -1,6 +1,6 @@ "use strict"; -const assert = require( "assert" ); +const assert = require( "node:assert/strict" ); // Check if the object we got is the jQuery object by invoking a basic API. const ensureJQuery = ( jQuery ) => { @@ -8,4 +8,4 @@ const ensureJQuery = ( jQuery ) => { "jQuery.expando was not detected, the jQuery bootstrap process has failed" ); }; -module.exports = ensureJQuery; +module.exports = { ensureJQuery }; diff --git a/test/node_smoke_tests/commonjs/lib/jquery-module-specifier.cjs b/test/node_smoke_tests/commonjs/lib/jquery-module-specifier.cjs new file mode 100644 index 0000000000..216660d80a --- /dev/null +++ b/test/node_smoke_tests/commonjs/lib/jquery-module-specifier.cjs @@ -0,0 +1,22 @@ +"use strict"; + +const path = require( "node:path" ); + +const ROOT_DIR = path.resolve( __dirname, "..", "..", "..", ".." ); + +// If `jQueryModuleSpecifier` is a real relative path, make it absolute +// to make sure it resolves to the same file inside utils from +// a subdirectory. Otherwise, leave it as-is as we may be testing `exports` +// so we need input as-is. +const getJQueryModuleSpecifier = () => { + const jQueryModuleInputSpecifier = process.argv[ 2 ]; + if ( !jQueryModuleInputSpecifier ) { + throw new Error( "jQuery module specifier not passed" ); + } + + return jQueryModuleInputSpecifier.startsWith( "." ) ? + path.resolve( ROOT_DIR, jQueryModuleInputSpecifier ) : + jQueryModuleInputSpecifier; +}; + +module.exports = { getJQueryModuleSpecifier }; diff --git a/test/node_smoke_tests/commonjs/regular/window_present_originally.cjs b/test/node_smoke_tests/commonjs/regular/window_present_originally.cjs new file mode 100644 index 0000000000..644a15a777 --- /dev/null +++ b/test/node_smoke_tests/commonjs/regular/window_present_originally.cjs @@ -0,0 +1,19 @@ +"use strict"; + +const { JSDOM } = require( "jsdom" ); + +const { ensureJQuery } = require( "../lib/ensure_jquery.cjs" ); +const { ensureGlobalNotCreated } = require( "../lib/ensure_global_not_created.cjs" ); +const { getJQueryModuleSpecifier } = require( "../lib/jquery-module-specifier.cjs" ); + +const jQueryModuleSpecifier = getJQueryModuleSpecifier(); + +const { window } = new JSDOM( "" ); + +// Set the window global. +globalThis.window = window; + +const jQuery = require( jQueryModuleSpecifier ); + +ensureJQuery( jQuery ); +ensureGlobalNotCreated( module.exports, window ); diff --git a/test/node_smoke_tests/document_missing.js b/test/node_smoke_tests/document_missing.js deleted file mode 100644 index 0f6a3f78b2..0000000000 --- a/test/node_smoke_tests/document_missing.js +++ /dev/null @@ -1,11 +0,0 @@ -"use strict"; - -const assert = require( "assert" ); -const ensureGlobalNotCreated = require( "./lib/ensure_global_not_created" ); -const jQueryFactory = require( "../../dist/jquery.js" ); - -assert.throws( () => { - jQueryFactory( {} ); -}, /jQuery requires a window with a document/ ); - -ensureGlobalNotCreated( module.exports ); diff --git a/test/node_smoke_tests/document_passed.js b/test/node_smoke_tests/document_passed.js deleted file mode 100644 index b1154d3a31..0000000000 --- a/test/node_smoke_tests/document_passed.js +++ /dev/null @@ -1,12 +0,0 @@ -"use strict"; - -const { JSDOM } = require( "jsdom" ); - -const { window } = new JSDOM( "" ); - -const ensureJQuery = require( "./lib/ensure_jquery" ); -const ensureGlobalNotCreated = require( "./lib/ensure_global_not_created" ); -const jQuery = require( "../../dist/jquery.js" )( window ); - -ensureJQuery( jQuery ); -ensureGlobalNotCreated( module.exports ); diff --git a/test/node_smoke_tests/document_present_originally.js b/test/node_smoke_tests/document_present_originally.js deleted file mode 100644 index 89b0c7bd87..0000000000 --- a/test/node_smoke_tests/document_present_originally.js +++ /dev/null @@ -1,15 +0,0 @@ -"use strict"; - -const { JSDOM } = require( "jsdom" ); - -const { window } = new JSDOM( "" ); - -// Pretend the window is a global. -global.window = window; - -const ensureJQuery = require( "./lib/ensure_jquery" ); -const ensureGlobalNotCreated = require( "./lib/ensure_global_not_created" ); -const jQuery = require( "../../dist/jquery.js" ); - -ensureJQuery( jQuery ); -ensureGlobalNotCreated( module.exports, window ); diff --git a/test/node_smoke_tests/dual/factory/import-and-require-factory.js b/test/node_smoke_tests/dual/factory/import-and-require-factory.js new file mode 100644 index 0000000000..c94ae374e7 --- /dev/null +++ b/test/node_smoke_tests/dual/factory/import-and-require-factory.js @@ -0,0 +1,17 @@ +import assert from "node:assert/strict"; +import { JSDOM } from "jsdom"; + +const { window } = new JSDOM( "" ); + +const { jQueryFactory: factoryImported } = await import( process.argv[ 2 ] ); +const { jQueryFactory: factoryRequired } = await import( "../lib/jquery-require-factory.cjs" ); + +assert( factoryImported === factoryRequired, + "More than one copy of jQueryFactory exists" ); + +assert( !( "expando" in factoryImported ), + "jQuery.expando should not be attached to the factory" ); + +const $ = factoryImported( window ); + +assert( /^jQuery/.test( $.expando ), "jQuery.expando should be detected" ); diff --git a/test/node_smoke_tests/dual/lib/jquery-require-factory.cjs b/test/node_smoke_tests/dual/lib/jquery-require-factory.cjs new file mode 100644 index 0000000000..55855b5ff5 --- /dev/null +++ b/test/node_smoke_tests/dual/lib/jquery-require-factory.cjs @@ -0,0 +1,5 @@ +"use strict"; + +const { jQueryFactory } = require( process.argv[ 2 ] ); + +module.exports.jQueryFactory = jQueryFactory; diff --git a/test/node_smoke_tests/dual/lib/jquery-require.cjs b/test/node_smoke_tests/dual/lib/jquery-require.cjs new file mode 100644 index 0000000000..7b46721ee5 --- /dev/null +++ b/test/node_smoke_tests/dual/lib/jquery-require.cjs @@ -0,0 +1,5 @@ +"use strict"; + +const $ = require( process.argv[ 2 ] ); + +module.exports.$ = $; diff --git a/test/node_smoke_tests/dual/regular/import-and-require.js b/test/node_smoke_tests/dual/regular/import-and-require.js new file mode 100644 index 0000000000..2b958a236f --- /dev/null +++ b/test/node_smoke_tests/dual/regular/import-and-require.js @@ -0,0 +1,13 @@ +import assert from "node:assert/strict"; +import { JSDOM } from "jsdom"; + +const { window } = new JSDOM( "" ); + +// Set the window global. +globalThis.window = window; + +const { $: $imported } = await import( process.argv[ 2 ] ); +const { $: $required } = await import( "../lib/jquery-require.cjs" ); + +assert( $imported === $required, "More than one copy of jQuery exists" ); +assert( /^jQuery/.test( $imported.expando ), "jQuery.expando should be detected" ); diff --git a/test/node_smoke_tests/iterable_with_native_symbol.js b/test/node_smoke_tests/iterable_with_native_symbol.js deleted file mode 100644 index 3376ebdc55..0000000000 --- a/test/node_smoke_tests/iterable_with_native_symbol.js +++ /dev/null @@ -1,8 +0,0 @@ -"use strict"; - -if ( typeof Symbol === "undefined" ) { - console.log( "Symbols not supported, skipping the test..." ); - process.exit(); -} - -require( "./lib/ensure_iterability_es6" )(); diff --git a/test/node_smoke_tests/lib/ensure_iterability_es6.js b/test/node_smoke_tests/lib/ensure_iterability_es6.js deleted file mode 100644 index a948f1996d..0000000000 --- a/test/node_smoke_tests/lib/ensure_iterability_es6.js +++ /dev/null @@ -1,25 +0,0 @@ -"use strict"; - -const assert = require( "assert" ); - -const ensureIterability = () => { - const { JSDOM } = require( "jsdom" ); - - const { window } = new JSDOM( "" ); - - let i; - const ensureJQuery = require( "./ensure_jquery" ); - const jQuery = require( "../../../dist/jquery.js" )( window ); - const elem = jQuery( "
" ); - let result = ""; - - ensureJQuery( jQuery ); - - for ( i of elem ) { - result += i.nodeName; - } - - assert.strictEqual( result, "DIVSPANA", "for-of works on jQuery objects" ); -}; - -module.exports = ensureIterability; diff --git a/test/node_smoke_tests/module/factory/document_missing.js b/test/node_smoke_tests/module/factory/document_missing.js new file mode 100644 index 0000000000..b902d29133 --- /dev/null +++ b/test/node_smoke_tests/module/factory/document_missing.js @@ -0,0 +1,13 @@ +import assert from "node:assert/strict"; + +import { ensureGlobalNotCreated } from "../lib/ensure_global_not_created.js"; +import { getJQueryModuleSpecifier } from "../lib/jquery-module-specifier.js"; + +const jQueryModuleSpecifier = getJQueryModuleSpecifier(); +const { jQueryFactory } = await import( jQueryModuleSpecifier ); + +assert.throws( () => { + jQueryFactory( {} ); +}, /jQuery requires a window with a document/ ); + +ensureGlobalNotCreated(); diff --git a/test/node_smoke_tests/module/factory/document_passed.js b/test/node_smoke_tests/module/factory/document_passed.js new file mode 100644 index 0000000000..800b34a4e5 --- /dev/null +++ b/test/node_smoke_tests/module/factory/document_passed.js @@ -0,0 +1,14 @@ +import { JSDOM } from "jsdom"; + +import { ensureJQuery } from "../lib/ensure_jquery.js"; +import { ensureGlobalNotCreated } from "../lib/ensure_global_not_created.js"; +import { getJQueryModuleSpecifier } from "../lib/jquery-module-specifier.js"; + +const { window } = new JSDOM( "" ); + +const jQueryModuleSpecifier = getJQueryModuleSpecifier(); +const { jQueryFactory } = await import( jQueryModuleSpecifier ); +const jQuery = jQueryFactory( window ); + +ensureJQuery( jQuery ); +ensureGlobalNotCreated(); diff --git a/test/node_smoke_tests/module/factory/iterable_with_native_symbol.js b/test/node_smoke_tests/module/factory/iterable_with_native_symbol.js new file mode 100644 index 0000000000..cb19c26c27 --- /dev/null +++ b/test/node_smoke_tests/module/factory/iterable_with_native_symbol.js @@ -0,0 +1,12 @@ +import process from "node:process"; + +import { ensureIterability } from "../lib/ensure_iterability_es6.js"; +import { getJQueryModuleSpecifier } from "../lib/jquery-module-specifier.js"; + +if ( typeof Symbol === "undefined" ) { + console.log( "Symbols not supported, skipping the test..." ); + process.exit(); +} + +const jQueryModuleSpecifier = getJQueryModuleSpecifier(); +await ensureIterability( jQueryModuleSpecifier ); diff --git a/test/node_smoke_tests/module/lib/ensure_global_not_created.js b/test/node_smoke_tests/module/lib/ensure_global_not_created.js new file mode 100644 index 0000000000..caccd82e36 --- /dev/null +++ b/test/node_smoke_tests/module/lib/ensure_global_not_created.js @@ -0,0 +1,11 @@ +import assert from "node:assert/strict"; + +// Ensure the jQuery property on global/window/module "this"/etc. was not +// created in a CommonJS environment. +// `global` is always checked in addition to passed parameters. +export const ensureGlobalNotCreated = ( ...args ) => { + [ ...args, global ].forEach( function( object ) { + assert.strictEqual( object.jQuery, undefined, + "A jQuery global was created in a module environment." ); + } ); +}; diff --git a/test/node_smoke_tests/module/lib/ensure_iterability_es6.js b/test/node_smoke_tests/module/lib/ensure_iterability_es6.js new file mode 100644 index 0000000000..c12e58c59f --- /dev/null +++ b/test/node_smoke_tests/module/lib/ensure_iterability_es6.js @@ -0,0 +1,21 @@ +import assert from "node:assert/strict"; +const { JSDOM } = await import( "jsdom" ); + +const { ensureJQuery } = await import( "./ensure_jquery.js" ); + +export const ensureIterability = async( jQueryModuleSpecifier ) => { + const { window } = new JSDOM( "" ); + + const { jQueryFactory } = await import( jQueryModuleSpecifier ); + const jQuery = jQueryFactory( window ); + const elem = jQuery( "
" ); + + ensureJQuery( jQuery ); + + let result = ""; + for ( const node of elem ) { + result += node.nodeName; + } + + assert.strictEqual( result, "DIVSPANA", "for-of works on jQuery objects" ); +}; diff --git a/test/node_smoke_tests/module/lib/ensure_jquery.js b/test/node_smoke_tests/module/lib/ensure_jquery.js new file mode 100644 index 0000000000..13749306ea --- /dev/null +++ b/test/node_smoke_tests/module/lib/ensure_jquery.js @@ -0,0 +1,7 @@ +import assert from "node:assert/strict"; + +// Check if the object we got is the jQuery object by invoking a basic API. +export const ensureJQuery = ( jQuery ) => { + assert( /^jQuery/.test( jQuery.expando ), + "jQuery.expando was not detected, the jQuery bootstrap process has failed" ); +}; diff --git a/test/node_smoke_tests/module/lib/jquery-module-specifier.js b/test/node_smoke_tests/module/lib/jquery-module-specifier.js new file mode 100644 index 0000000000..e65432f47f --- /dev/null +++ b/test/node_smoke_tests/module/lib/jquery-module-specifier.js @@ -0,0 +1,26 @@ +import path from "node:path"; +import { fileURLToPath } from "node:url"; + +const dirname = path.dirname( fileURLToPath( import.meta.url ) ); + +const ROOT_DIR = path.resolve( dirname, "..", "..", "..", ".." ); + +// import does not work with Windows-style paths +function ensureUnixPath( path ) { + return path.replace( /^[a-z]:/i, "" ).replace( /\\+/g, "/" ); +} + +// If `jQueryModuleSpecifier` is a real relative path, make it absolute +// to make sure it resolves to the same file inside utils from +// a subdirectory. Otherwise, leave it as-is as we may be testing `exports` +// so we need input as-is. +export const getJQueryModuleSpecifier = () => { + const jQueryModuleInputSpecifier = process.argv[ 2 ]; + if ( !jQueryModuleInputSpecifier ) { + throw new Error( "jQuery module specifier not passed" ); + } + + return jQueryModuleInputSpecifier.startsWith( "." ) ? + ensureUnixPath( path.resolve( ROOT_DIR, jQueryModuleInputSpecifier ) ) : + jQueryModuleInputSpecifier; +}; diff --git a/test/node_smoke_tests/module/regular/window_present_originally.js b/test/node_smoke_tests/module/regular/window_present_originally.js new file mode 100644 index 0000000000..fee3ca5aef --- /dev/null +++ b/test/node_smoke_tests/module/regular/window_present_originally.js @@ -0,0 +1,17 @@ +import { JSDOM } from "jsdom"; + +import { ensureJQuery } from "../lib/ensure_jquery.js"; +import { ensureGlobalNotCreated } from "../lib/ensure_global_not_created.js"; +import { getJQueryModuleSpecifier } from "../lib/jquery-module-specifier.js"; + +const jQueryModuleSpecifier = getJQueryModuleSpecifier(); + +const { window } = new JSDOM( "" ); + +// Set the window global. +globalThis.window = window; + +const { jQuery } = await import( jQueryModuleSpecifier ); + +ensureJQuery( jQuery ); +ensureGlobalNotCreated( window ); diff --git a/test/promises_aplus_adapters/.eslintrc.json b/test/promises_aplus_adapters/.eslintrc.json deleted file mode 100644 index f961645e55..0000000000 --- a/test/promises_aplus_adapters/.eslintrc.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "root": true, - - "extends": "../../.eslintrc-node.json" -} diff --git a/test/promises_aplus_adapters/deferred.js b/test/promises_aplus_adapters/deferred.cjs similarity index 76% rename from test/promises_aplus_adapters/deferred.js rename to test/promises_aplus_adapters/deferred.cjs index 5e3ffe2d85..10e8245613 100644 --- a/test/promises_aplus_adapters/deferred.js +++ b/test/promises_aplus_adapters/deferred.cjs @@ -4,7 +4,8 @@ const { JSDOM } = require( "jsdom" ); const { window } = new JSDOM( "" ); -const jQuery = require( "../../" )( window ); +const { jQueryFactory } = require( "jquery/factory" ); +const jQuery = jQueryFactory( window ); module.exports.deferred = () => { const deferred = jQuery.Deferred(); diff --git a/test/promises_aplus_adapters/when.js b/test/promises_aplus_adapters/when.cjs similarity index 90% rename from test/promises_aplus_adapters/when.js rename to test/promises_aplus_adapters/when.cjs index 3e945d4755..8272168037 100644 --- a/test/promises_aplus_adapters/when.js +++ b/test/promises_aplus_adapters/when.cjs @@ -4,7 +4,8 @@ const { JSDOM } = require( "jsdom" ); const { window } = new JSDOM( "" ); -const jQuery = require( "../../" )( window ); +const { jQueryFactory } = require( "jquery/factory" ); +const jQuery = jQueryFactory( window ); module.exports.deferred = () => { let adopted, promised; diff --git a/test/unit/ajax.js b/test/unit/ajax.js index d06ce99750..4f843f2e53 100644 --- a/test/unit/ajax.js +++ b/test/unit/ajax.js @@ -13,7 +13,7 @@ QUnit.module( "ajax", { assert.ok( !isLocal, "Unit tests are not ran from file:// (especially in Chrome. If you must test from file:// with Chrome, run it with the --allow-file-access-from-files flag!)" ); } ); - if ( !jQuery.ajax || ( isLocal && !hasPHP ) ) { + if ( !includesModule( "ajax" ) || ( isLocal && !hasPHP ) ) { return; } @@ -71,35 +71,91 @@ QUnit.module( "ajax", { }; } ); - ajaxTest( "jQuery.ajax() - custom attributes for script tag", 5, - function( assert ) { - return { - create: function( options ) { - var xhr; - options.method = "POST"; - options.dataType = "script"; - options.scriptAttrs = { id: "jquery-ajax-test", async: "async" }; - xhr = jQuery.ajax( url( "mock.php?action=script" ), options ); - assert.equal( jQuery( "#jquery-ajax-test" ).attr( "async" ), "async", "attr value" ); - return xhr; - }, - beforeSend: function( _jqXhr, settings ) { - assert.strictEqual( settings.type, "GET", "Type changed to GET" ); - }, - success: function() { - assert.ok( true, "success" ); - }, - complete: function() { - assert.ok( true, "complete" ); - } - }; - } - ); + jQuery.each( [ " - Same Domain", " - Cross Domain" ], function( crossDomain, label ) { + ajaxTest( "jQuery.ajax() - custom attributes for script tag" + label, 5, + function( assert ) { + return { + create: function( options ) { + var xhr; + options.crossDomain = crossDomain; + options.method = "POST"; + options.dataType = "script"; + options.scriptAttrs = { id: "jquery-ajax-test", async: "async" }; + xhr = jQuery.ajax( url( "mock.php?action=script" ), options ); + assert.equal( jQuery( "#jquery-ajax-test" ).attr( "async" ), "async", "attr value" ); + return xhr; + }, + beforeSend: function( _jqXhr, settings ) { + assert.strictEqual( settings.type, "GET", "Type changed to GET" ); + }, + success: function() { + assert.ok( true, "success" ); + }, + complete: function() { + assert.ok( true, "complete" ); + } + }; + } + ); + + ajaxTest( "jQuery.ajax() - headers for script transport" + label, 3, + function( assert ) { + return { + create: function( options ) { + Globals.register( "corsCallback" ); + window.corsCallback = function( response ) { + assert.strictEqual( response.headers[ "x-custom-test-header" ], + "test value", "Custom header sent" ); + }; + options.crossDomain = crossDomain; + options.dataType = "script"; + options.headers = { "x-custom-test-header": "test value" }; + return jQuery.ajax( url( "mock.php?action=script&callback=corsCallback" ), options ); + }, + success: function() { + assert.ok( true, "success" ); + }, + complete: function() { + assert.ok( true, "complete" ); + } + }; + } + ); + + ajaxTest( "jQuery.ajax() - scriptAttrs winning over headers" + label, 4, + function( assert ) { + return { + create: function( options ) { + var xhr; + Globals.register( "corsCallback" ); + window.corsCallback = function( response ) { + assert.ok( !response.headers[ "x-custom-test-header" ], + "headers losing with scriptAttrs" ); + }; + options.crossDomain = crossDomain; + options.dataType = "script"; + options.scriptAttrs = { id: "jquery-ajax-test", async: "async" }; + options.headers = { "x-custom-test-header": "test value" }; + xhr = jQuery.ajax( url( "mock.php?action=script&callback=corsCallback" ), options ); + assert.equal( jQuery( "#jquery-ajax-test" ).attr( "async" ), "async", "attr value" ); + return xhr; + }, + success: function() { + assert.ok( true, "success" ); + }, + complete: function() { + assert.ok( true, "complete" ); + } + }; + } + ); + } ); ajaxTest( "jQuery.ajax() - execute JS when dataType option is provided", 3, function( assert ) { return { create: function( options ) { + Globals.register( "corsCallback" ); options.crossDomain = true; options.dataType = "script"; return jQuery.ajax( url( "mock.php?action=script&header=ecma" ), options ); @@ -341,7 +397,7 @@ QUnit.module( "ajax", { }; } ); - ajaxTest( "jQuery.ajax() - URL fragment component preservation", 4, function( assert ) { + ajaxTest( "jQuery.ajax() - URL fragment component preservation", 5, function( assert ) { return [ { url: baseURL + "name.html#foo", @@ -373,6 +429,25 @@ QUnit.module( "ajax", { }, error: true }, + { + url: baseURL + "name.html?abc#foo", + data: [ + { + name: "test", + value: 123 + }, + { + name: "devo", + value: "hat" + } + ], + beforeSend: function( xhr, settings ) { + assert.equal( settings.url, baseURL + "name.html?abc&test=123&devo=hat#foo", + "hash preserved for request with query component and array data." ); + return false; + }, + error: true + }, { url: baseURL + "name.html?abc#brownies", data: { @@ -380,6 +455,7 @@ QUnit.module( "ajax", { }, cache: false, beforeSend: function( xhr, settings ) { + // Clear the cache-buster param value var url = settings.url.replace( /_=[^&#]+/, "_=" ); assert.equal( url, baseURL + "name.html?abc&devo=hat&_=#brownies", @@ -609,7 +685,7 @@ QUnit.module( "ajax", { url: url( "404.txt" ), beforeSend: nocallback( "beforeSend" ), error: nocallback( "error" ), - complete: nocallback( "complete" ) + complete: nocallback( "complete" ) }; } ); @@ -798,8 +874,8 @@ QUnit.module( "ajax", { success: function( data ) { assert.ok( data.match( /^html text/ ), "Check content for datatype html" ); jQuery( "#ap" ).html( data ); - assert.strictEqual( window[ "testFoo" ], "foo", "Check if script was evaluated for datatype html" ); - assert.strictEqual( window[ "testBar" ], "bar", "Check if script src was evaluated for datatype html" ); + assert.strictEqual( window.testFoo, "foo", "Check if script was evaluated for datatype html" ); + assert.strictEqual( window.testBar, "bar", "Check if script src was evaluated for datatype html" ); } }; } ); @@ -809,6 +885,7 @@ QUnit.module( "ajax", { return { dataType: "jsonp", url: url( "mock.php?action=errorWithScript" ), + // error is the significant assertion error: function( xhr ) { var expected = { "status": 404, "msg": "Not Found" }; @@ -837,6 +914,7 @@ QUnit.module( "ajax", { complete: function() { jQuery.globalEval = globalEval; }, + // error is the significant assertion error: function( xhr ) { assert.strictEqual( xhr.status, 404, testMsg ); @@ -1096,8 +1174,8 @@ QUnit.module( "ajax", { Globals.register( "functionToCleanUp" ); Globals.register( "XXX" ); Globals.register( "jsonpResults" ); - window[ "jsonpResults" ] = function( data ) { - assert.ok( data[ "data" ], "JSON results returned (GET, custom callback function)" ); + window.jsonpResults = function( data ) { + assert.ok( data.data, "JSON results returned (GET, custom callback function)" ); }; }, requests: [ { @@ -1106,7 +1184,7 @@ QUnit.module( "ajax", { crossDomain: crossDomain, jsonp: "callback", success: function( data ) { - assert.ok( data[ "data" ], "JSON results returned (GET, data obj callback)" ); + assert.ok( data.data, "JSON results returned (GET, data obj callback)" ); } }, { url: baseURL + "mock.php?action=jsonp", @@ -1115,7 +1193,7 @@ QUnit.module( "ajax", { jsonpCallback: "jsonpResults", success: function( data ) { assert.strictEqual( - typeof window[ "jsonpResults" ], + typeof window.jsonpResults, "function", "should not rewrite original function" ); @@ -1127,8 +1205,8 @@ QUnit.module( "ajax", { crossDomain: crossDomain, jsonpCallback: "functionToCleanUp", success: function( data ) { - assert.ok( data[ "data" ], "JSON results returned (GET, custom callback name to be cleaned up)" ); - assert.strictEqual( window[ "functionToCleanUp" ], true, "Callback was removed (GET, custom callback name to be cleaned up)" ); + assert.ok( data.data, "JSON results returned (GET, custom callback name to be cleaned up)" ); + assert.strictEqual( window.functionToCleanUp, true, "Callback was removed (GET, custom callback name to be cleaned up)" ); var xhr; jQuery.ajax( { url: baseURL + "mock.php?action=jsonp", @@ -1142,7 +1220,7 @@ QUnit.module( "ajax", { } ); xhr.fail( function() { assert.ok( true, "Ajax error JSON (GET, custom callback name to be cleaned up)" ); - assert.strictEqual( window[ "functionToCleanUp" ], true, "Callback was removed after early abort (GET, custom callback name to be cleaned up)" ); + assert.strictEqual( window.functionToCleanUp, true, "Callback was removed after early abort (GET, custom callback name to be cleaned up)" ); } ); } }, { @@ -1155,7 +1233,7 @@ QUnit.module( "ajax", { assert.ok( /action=jsonp&callback=XXX&_=\d+$/.test( this.url ), "The URL wasn't messed with (GET, custom callback name with no url manipulation)" ); }, success: function( data ) { - assert.ok( data[ "data" ], "JSON results returned (GET, custom callback name with no url manipulation)" ); + assert.ok( data.data, "JSON results returned (GET, custom callback name with no url manipulation)" ); } } ] }; @@ -1192,7 +1270,7 @@ QUnit.module( "ajax", { dataType: "jsonp", crossDomain: crossDomain, success: function( data ) { - assert.ok( data[ "data" ], "JSON results returned (POST, no callback)" ); + assert.ok( data.data, "JSON results returned (POST, no callback)" ); } }, { @@ -1202,7 +1280,7 @@ QUnit.module( "ajax", { dataType: "jsonp", crossDomain: crossDomain, success: function( data ) { - assert.ok( data[ "data" ], "JSON results returned (POST, data callback)" ); + assert.ok( data.data, "JSON results returned (POST, data callback)" ); } }, { @@ -1212,7 +1290,7 @@ QUnit.module( "ajax", { dataType: "jsonp", crossDomain: crossDomain, success: function( data ) { - assert.ok( data[ "data" ], "JSON results returned (POST, data obj callback)" ); + assert.ok( data.data, "JSON results returned (POST, data obj callback)" ); } } ]; @@ -1386,7 +1464,7 @@ QUnit.module( "ajax", { url: url( "mock.php?action=testbar" ), dataType: "script", success: function() { - assert.strictEqual( window[ "testBar" ], "bar", "Script results returned (GET, no callback)" ); + assert.strictEqual( window.testBar, "bar", "Script results returned (GET, no callback)" ); } }; } ); @@ -1403,7 +1481,7 @@ QUnit.module( "ajax", { type: "POST", dataType: "script", success: function( data, status ) { - assert.strictEqual( window[ "testBar" ], "bar", "Script results returned (POST, no callback)" ); + assert.strictEqual( window.testBar, "bar", "Script results returned (POST, no callback)" ); assert.strictEqual( status, "success", "Script results returned (POST, no callback)" ); } }; @@ -1417,7 +1495,7 @@ QUnit.module( "ajax", { url: url( "mock.php?action=testbar" ), dataType: "script", success: function() { - assert.strictEqual( window[ "testBar" ], "bar", "Script results returned (GET, no callback)" ); + assert.strictEqual( window.testBar, "bar", "Script results returned (GET, no callback)" ); } }; } ); @@ -1433,43 +1511,92 @@ QUnit.module( "ajax", { }; } ); - ajaxTest( "jQuery.ajax() - JSON by content-type", 5, function( assert ) { - return { - url: baseURL + "mock.php?action=json", - data: { - "header": "json", - "array": "1" + ajaxTest( "jQuery.ajax() - JSON by content-type", 10, function( assert ) { + return [ + { + url: baseURL + "mock.php?action=json", + data: { + "header": "json", + "array": "1" + }, + success: function( json ) { + assert.ok( json.length >= 2, "Check length" ); + assert.strictEqual( json[ 0 ].name, "John", "Check JSON: first, name" ); + assert.strictEqual( json[ 0 ].age, 21, "Check JSON: first, age" ); + assert.strictEqual( json[ 1 ].name, "Peter", "Check JSON: second, name" ); + assert.strictEqual( json[ 1 ].age, 25, "Check JSON: second, age" ); + } }, - success: function( json ) { - assert.ok( json.length >= 2, "Check length" ); - assert.strictEqual( json[ 0 ][ "name" ], "John", "Check JSON: first, name" ); - assert.strictEqual( json[ 0 ][ "age" ], 21, "Check JSON: first, age" ); - assert.strictEqual( json[ 1 ][ "name" ], "Peter", "Check JSON: second, name" ); - assert.strictEqual( json[ 1 ][ "age" ], 25, "Check JSON: second, age" ); + { + url: baseURL + "mock.php?action=json", + data: [ + { + name: "header", + value: "json" + }, + { + name: "array", + value: "1" + } + ], + success: function( json ) { + assert.ok( json.length >= 2, "Check length" ); + assert.strictEqual( json[ 0 ].name, "John", "Check JSON: first, name" ); + assert.strictEqual( json[ 0 ].age, 21, "Check JSON: first, age" ); + assert.strictEqual( json[ 1 ].name, "Peter", "Check JSON: second, name" ); + assert.strictEqual( json[ 1 ].age, 25, "Check JSON: second, age" ); + } } - }; + ]; } ); - ajaxTest( "jQuery.ajax() - JSON by content-type disabled with options", 6, function( assert ) { - return { - url: url( "mock.php?action=json" ), - data: { - "header": "json", - "array": "1" - }, - contents: { - "json": false + ajaxTest( "jQuery.ajax() - JSON by content-type disabled with options", 12, function( assert ) { + return [ + { + url: url( "mock.php?action=json" ), + data: { + "header": "json", + "array": "1" + }, + contents: { + "json": false + }, + success: function( text ) { + assert.strictEqual( typeof text, "string", "json wasn't auto-determined" ); + var json = JSON.parse( text ); + assert.ok( json.length >= 2, "Check length" ); + assert.strictEqual( json[ 0 ].name, "John", "Check JSON: first, name" ); + assert.strictEqual( json[ 0 ].age, 21, "Check JSON: first, age" ); + assert.strictEqual( json[ 1 ].name, "Peter", "Check JSON: second, name" ); + assert.strictEqual( json[ 1 ].age, 25, "Check JSON: second, age" ); + } }, - success: function( text ) { - assert.strictEqual( typeof text, "string", "json wasn't auto-determined" ); - var json = JSON.parse( text ); - assert.ok( json.length >= 2, "Check length" ); - assert.strictEqual( json[ 0 ][ "name" ], "John", "Check JSON: first, name" ); - assert.strictEqual( json[ 0 ][ "age" ], 21, "Check JSON: first, age" ); - assert.strictEqual( json[ 1 ][ "name" ], "Peter", "Check JSON: second, name" ); - assert.strictEqual( json[ 1 ][ "age" ], 25, "Check JSON: second, age" ); + { + url: url( "mock.php?action=json" ), + data: [ + { + name: "header", + value: "json" + }, + { + name: "array", + value: "1" + } + ], + contents: { + "json": false + }, + success: function( text ) { + assert.strictEqual( typeof text, "string", "json wasn't auto-determined" ); + var json = JSON.parse( text ); + assert.ok( json.length >= 2, "Check length" ); + assert.strictEqual( json[ 0 ].name, "John", "Check JSON: first, name" ); + assert.strictEqual( json[ 0 ].age, 21, "Check JSON: first, age" ); + assert.strictEqual( json[ 1 ].name, "Peter", "Check JSON: second, name" ); + assert.strictEqual( json[ 1 ].age, 25, "Check JSON: second, age" ); + } } - }; + ]; } ); ajaxTest( "jQuery.ajax() - simple get", 1, function( assert ) { @@ -1517,18 +1644,36 @@ QUnit.module( "ajax", { }; } ); - ajaxTest( "jQuery.ajax() - data - text/plain (gh-2658)", 1, function( assert ) { - return { - url: "bogus.html", - data: { devo: "A Beautiful World" }, - type: "post", - contentType: "text/plain", - beforeSend: function( _, s ) { - assert.strictEqual( s.data, "devo=A%20Beautiful%20World", "data is %20-encoded" ); - return false; + ajaxTest( "jQuery.ajax() - data - text/plain (gh-2658)", 2, function( assert ) { + return [ + { + url: "bogus.html", + data: { devo: "A Beautiful World" }, + type: "post", + contentType: "text/plain", + beforeSend: function( _, s ) { + assert.strictEqual( s.data, "devo=A%20Beautiful%20World", "data is %20-encoded" ); + return false; + }, + error: true }, - error: true - }; + { + url: "bogus.html", + data: [ + { + name: "devo", + value: "A Beautiful World" + } + ], + type: "post", + contentType: "text/plain", + beforeSend: function( _, s ) { + assert.strictEqual( s.data, "devo=A%20Beautiful%20World", "data is %20-encoded" ); + return false; + }, + error: true + } + ]; } ); ajaxTest( "jQuery.ajax() - don't escape %20 with contentType override (gh-4119)", 1, function( assert ) { @@ -1577,34 +1722,82 @@ QUnit.module( "ajax", { }; } ); - ajaxTest( "jQuery.ajax() - data - no processing POST", 1, function( assert ) { - return { - url: "bogus.html", - data: { devo: "A Beautiful World" }, - type: "post", - contentType: "x-special-sauce", - processData: false, - beforeSend: function( _, s ) { - assert.deepEqual( s.data, { devo: "A Beautiful World" }, "data is not processed" ); - return false; + ajaxTest( "jQuery.ajax() - data - no processing POST", 2, function( assert ) { + return [ + { + url: "bogus.html", + data: { devo: "A Beautiful World" }, + type: "post", + contentType: "x-special-sauce", + processData: false, + beforeSend: function( _, s ) { + assert.deepEqual( s.data, { devo: "A Beautiful World" }, "data is not processed" ); + return false; + }, + error: true }, - error: true - }; + { + url: "bogus.html", + data: [ + { + name: "devo", + value: "A Beautiful World" + } + ], + type: "post", + contentType: "x-special-sauce", + processData: false, + beforeSend: function( _, s ) { + assert.deepEqual( s.data, [ + { + name: "devo", + value: "A Beautiful World" + } + ], "data is not processed" ); + return false; + }, + error: true + } + ]; } ); - ajaxTest( "jQuery.ajax() - data - no processing GET", 1, function( assert ) { - return { - url: "bogus.html", - data: { devo: "A Beautiful World" }, - type: "get", - contentType: "x-something-else", - processData: false, - beforeSend: function( _, s ) { - assert.deepEqual( s.data, { devo: "A Beautiful World" }, "data is not processed" ); - return false; + ajaxTest( "jQuery.ajax() - data - no processing GET", 2, function( assert ) { + return [ + { + url: "bogus.html", + data: { devo: "A Beautiful World" }, + type: "get", + contentType: "x-something-else", + processData: false, + beforeSend: function( _, s ) { + assert.deepEqual( s.data, { devo: "A Beautiful World" }, "data is not processed" ); + return false; + }, + error: true }, - error: true - }; + { + url: "bogus.html", + data: [ + { + name: "devo", + value: "A Beautiful World" + } + ], + type: "get", + contentType: "x-something-else", + processData: false, + beforeSend: function( _, s ) { + assert.deepEqual( s.data, [ + { + name: "devo", + value: "A Beautiful World" + } + ], "data is not processed" ); + return false; + }, + error: true + } + ]; } ); ajaxTest( "jQuery.ajax() - data - process string with GET", 2, function( assert ) { @@ -1626,6 +1819,7 @@ QUnit.module( "ajax", { var ifModifiedNow = new Date(); jQuery.each( + /* jQuery.each arguments start */ { " (cache)": true, @@ -1635,23 +1829,15 @@ QUnit.module( "ajax", { jQuery.each( { "If-Modified-Since": { - url: "mock.php?action=ims", - qunitMethod: "test" + url: "mock.php?action=ims" }, "Etag": { - url: "mock.php?action=etag", - - // Support: TestSwarm - // TestSwarm is now proxied via Cloudflare which cuts out - // headers relevant for ETag tests, failing them. We're still - // running those tests in Karma on Chrome & Firefox (including - // Firefox ESR). - qunitMethod: QUnit.isSwarm ? "skip" : "test" + url: "mock.php?action=etag" } }, function( type, data ) { var url = baseURL + data.url + "&ts=" + ifModifiedNow++; - QUnit[ data.qunitMethod ]( "jQuery.ajax() - " + type + + QUnit.test( "jQuery.ajax() - " + type + " support" + label, function( assert ) { assert.expect( 4 ); var done = assert.async(); @@ -1680,6 +1866,7 @@ QUnit.module( "ajax", { } ); } + /* jQuery.each arguments end */ ); @@ -1750,6 +1937,7 @@ QUnit.module( "ajax", { } jQuery.each( + /* jQuery.each arguments start */ { "name.html": true, @@ -1824,6 +2012,7 @@ QUnit.module( "ajax", { } ); } + /* jQuery.each arguments end*/ ); } ); @@ -2270,7 +2459,7 @@ if ( typeof window.ArrayBuffer === "undefined" || typeof new XMLHttpRequest().re // beforeunload, unload, pagehide, and visibilitychange event handlers. // See https://bugs.chromium.org/p/chromium/issues/detail?id=952452 // Safari 13 did similar changes. The below check will catch them both. - if ( !/safari/i.test( navigator.userAgent ) ) { + if ( !/webkit/i.test( navigator.userAgent ) ) { testIframe( "trac-14379 - jQuery.ajax() on unload", "ajax/onunload.html", @@ -2502,9 +2691,9 @@ if ( typeof window.ArrayBuffer === "undefined" || typeof new XMLHttpRequest().re } ); } ); -//----------- jQuery.domManip() +//----------- domManip() - QUnit.test( "trac-11264 - jQuery.domManip() - no side effect because of ajaxSetup or global events", function( assert ) { + QUnit.test( "trac-11264 - domManip() - no side effect because of ajaxSetup or global events", function( assert ) { assert.expect( 1 ); jQuery.ajaxSetup( { @@ -2521,7 +2710,7 @@ if ( typeof window.ArrayBuffer === "undefined" || typeof new XMLHttpRequest().re } ); QUnit.test( - "jQuery#load() - always use GET method even if it overrided through ajaxSetup (trac-11264)", + "jQuery#load() - always use GET method even if overridden through ajaxSetup (trac-11264)", function( assert ) { assert.expect( 1 ); var done = assert.async(); @@ -2558,7 +2747,7 @@ if ( typeof window.ArrayBuffer === "undefined" || typeof new XMLHttpRequest().re ); QUnit.test( - "trac-11402 - jQuery.domManip() - script in comments are properly evaluated", + "trac-11402 - domManip() - script in comments are properly evaluated", function( assert ) { assert.expect( 2 ); jQuery( "#qunit-fixture" ).load( baseURL + "cleanScript.html", assert.async() ); @@ -2593,6 +2782,57 @@ if ( typeof window.ArrayBuffer === "undefined" || typeof new XMLHttpRequest().re } ); } ); + QUnit.test( "jQuery.get( String, null, String ) - dataType with null callback (gh-4989)", + function( assert ) { + assert.expect( 2 ); + var done = assert.async( 2 ); + + jQuery.get( url( "mock.php?action=json&header" ), null, "json" ) + .then( function( json ) { + assert.deepEqual( json, { data: { lang: "en", length: 25 } }, + "`dataType: \"json\"` applied with a `null` callback" ); + done(); + } ); + + jQuery.get( url( "mock.php?action=json&header" ), null, "text" ) + .then( function( text ) { + assert.strictEqual( text, "{\"data\":{\"lang\":\"en\",\"length\":25}}", + "`dataType: \"text\"` applied with a `null` callback" ); + done(); + } ); + } ); + + QUnit.test( "jQuery.get( String, null-ish, null-ish, String ) - dataType with null/undefined data & callback", + function( assert ) { + assert.expect( 8 ); + var done = assert.async( 8 ); + + [ + { data: null, success: null }, + { data: null, success: undefined }, + { data: undefined, success: null }, + { data: undefined, success: undefined } + ].forEach( function( options ) { + var data = options.data, + success = options.success; + jQuery.get( url( "mock.php?action=json&header" ), data, success, "json" ) + .then( function( json ) { + assert.deepEqual( json, { data: { lang: "en", length: 25 } }, + "`dataType: \"json\"` applied with `" + data + "` data & `" + + success + "` success callback" ); + done(); + } ); + + jQuery.get( url( "mock.php?action=json&header" ), data, success, "text" ) + .then( function( text ) { + assert.strictEqual( text, "{\"data\":{\"lang\":\"en\",\"length\":25}}", + "`dataType: \"text\"` applied with `" + data + "` data & `" + + success + "` success callback" ); + done(); + } ); + } ); + } ); + //----------- jQuery.getJSON() QUnit.test( "jQuery.getJSON( String, Hash, Function ) - JSON array", function( assert ) { @@ -2605,10 +2845,10 @@ if ( typeof window.ArrayBuffer === "undefined" || typeof new XMLHttpRequest().re }, function( json ) { assert.ok( json.length >= 2, "Check length" ); - assert.strictEqual( json[ 0 ][ "name" ], "John", "Check JSON: first, name" ); - assert.strictEqual( json[ 0 ][ "age" ], 21, "Check JSON: first, age" ); - assert.strictEqual( json[ 1 ][ "name" ], "Peter", "Check JSON: second, name" ); - assert.strictEqual( json[ 1 ][ "age" ], 25, "Check JSON: second, age" ); + assert.strictEqual( json[ 0 ].name, "John", "Check JSON: first, name" ); + assert.strictEqual( json[ 0 ].age, 21, "Check JSON: first, age" ); + assert.strictEqual( json[ 1 ].name, "Peter", "Check JSON: second, name" ); + assert.strictEqual( json[ 1 ].age, 25, "Check JSON: second, age" ); done(); } ); @@ -2618,9 +2858,9 @@ if ( typeof window.ArrayBuffer === "undefined" || typeof new XMLHttpRequest().re assert.expect( 2 ); var done = assert.async(); jQuery.getJSON( url( "mock.php?action=json" ), function( json ) { - if ( json && json[ "data" ] ) { - assert.strictEqual( json[ "data" ][ "lang" ], "en", "Check JSON: lang" ); - assert.strictEqual( json[ "data" ].length, 25, "Check JSON: length" ); + if ( json && json.data ) { + assert.strictEqual( json.data.lang, "en", "Check JSON: lang" ); + assert.strictEqual( json.data.length, 25, "Check JSON: length" ); done(); } } ); @@ -2660,7 +2900,7 @@ if ( typeof window.ArrayBuffer === "undefined" || typeof new XMLHttpRequest().re Globals.register( "testBar" ); jQuery.getScript( url( "mock.php?action=testbar" ), function() { - assert.strictEqual( window[ "testBar" ], "bar", "Check if script was evaluated" ); + assert.strictEqual( window.testBar, "bar", "Check if script was evaluated" ); done(); } ); } @@ -2691,7 +2931,7 @@ if ( typeof window.ArrayBuffer === "undefined" || typeof new XMLHttpRequest().re jQuery.getScript( { url: url( "mock.php?action=testbar" ), success: function() { - assert.strictEqual( window[ "testBar" ], "bar", "Check if script was evaluated" ); + assert.strictEqual( window.testBar, "bar", "Check if script was evaluated" ); done(); } } ); @@ -2793,7 +3033,7 @@ if ( typeof window.ArrayBuffer === "undefined" || typeof new XMLHttpRequest().re assert.expect( 7 ); var done = assert.async(); var verifyEvaluation = function() { - assert.strictEqual( window[ "testBar" ], "bar", "Check if script src was evaluated after load" ); + assert.strictEqual( window.testBar, "bar", "Check if script src was evaluated after load" ); assert.strictEqual( jQuery( "#ap" ).html(), "bar", "Check if script evaluation has modified DOM" ); done(); }; @@ -2804,7 +3044,7 @@ if ( typeof window.ArrayBuffer === "undefined" || typeof new XMLHttpRequest().re jQuery( "#first" ).load( url( "mock.php?action=testHTML&baseURL=" + baseURL ), function() { assert.ok( jQuery( "#first" ).html().match( /^html text/ ), "Check content after loading html" ); assert.strictEqual( jQuery( "#foo" ).html(), "foo", "Check if script evaluation has modified DOM" ); - assert.strictEqual( window[ "testFoo" ], "foo", "Check if script was evaluated after load" ); + assert.strictEqual( window.testFoo, "foo", "Check if script was evaluated after load" ); setTimeout( verifyEvaluation, 600 ); } ); } ); @@ -2816,7 +3056,7 @@ if ( typeof window.ArrayBuffer === "undefined" || typeof new XMLHttpRequest().re jQuery( "#first" ).load( url( "test2.html" ), function() { assert.strictEqual( jQuery( "#foo" ).html(), "foo", "Check if script evaluation has modified DOM" ); - assert.strictEqual( window[ "testFoo" ], "foo", "Check if script was evaluated after load" ); + assert.strictEqual( window.testFoo, "foo", "Check if script was evaluated after load" ); done(); } ); } ); @@ -3029,4 +3269,70 @@ if ( typeof window.ArrayBuffer === "undefined" || typeof new XMLHttpRequest().re assert.ok( jQuery.active === 0, "ajax active counter should be zero: " + jQuery.active ); } ); + ajaxTest( "jQuery.ajax() - FormData", 1, function( assert ) { + var formData = new FormData(); + formData.append( "key1", "value1" ); + formData.append( "key2", "value2" ); + + return { + url: url( "mock.php?action=formData" ), + method: "post", + data: formData, + success: function( data ) { + assert.strictEqual( data, "key1 -> value1, key2 -> value2", + "FormData sent correctly" ); + } + }; + } ); + + ajaxTest( "jQuery.ajax() - URLSearchParams", 1, function( assert ) { + var urlSearchParams = new URLSearchParams(); + urlSearchParams.append( "name", "peter" ); + + return { + url: url( "mock.php?action=name" ), + method: "post", + data: urlSearchParams, + success: function( data ) { + assert.strictEqual( data, "pan", "URLSearchParams sent correctly" ); + } + }; + }, QUnit.testUnlessIE ); + + ajaxTest( "jQuery.ajax() - Blob", 1, function( assert ) { + var blob = new Blob( [ "name=peter" ], { type: "text/plain" } ); + + return { + url: url( "mock.php?action=name" ), + method: "post", + data: blob, + success: function( data ) { + assert.strictEqual( data, "pan", "Blob sent correctly" ); + } + }; + } ); + + ajaxTest( "jQuery.ajax() - non-plain object", 1, function( assert ) { + return { + url: url( "mock.php?action=name" ), + method: "post", + data: Object.create( { name: "peter" } ), + success: function( data ) { + assert.strictEqual( data, "ERROR", "Data correctly not sent" ); + } + }; + } ); + + ajaxTest( "jQuery.ajax() - non-plain object with processData: true", 1, function( assert ) { + return { + url: url( "mock.php?action=name" ), + method: "post", + processData: true, + data: Object.create( { name: "peter" } ), + success: function( data ) { + assert.strictEqual( data, "pan", "Data sent correctly" ); + } + }; + } ); + } )(); diff --git a/test/unit/animation.js b/test/unit/animation.js index d57a21b7a0..455c674a27 100644 --- a/test/unit/animation.js +++ b/test/unit/animation.js @@ -1,7 +1,7 @@ ( function() { // Can't test what ain't there -if ( !jQuery.fx ) { +if ( !includesModule( "effects" ) ) { return; } @@ -91,7 +91,7 @@ QUnit.test( "Animation.prefilter( fn ) - calls prefilter after defaultPrefilter" assert.expect( 1 ); var prefilter = this.sandbox.stub(), - defaultSpy = this.sandbox.spy( jQuery.Animation.prefilters, 0 ); + defaultSpy = this.sandbox.spy( jQuery.Animation.prefilters, "0" ); jQuery.Animation.prefilter( prefilter ); @@ -105,7 +105,7 @@ QUnit.test( "Animation.prefilter( fn, true ) - calls prefilter before defaultPre assert.expect( 1 ); var prefilter = this.sandbox.stub(), - defaultSpy = this.sandbox.spy( jQuery.Animation.prefilters, 0 ); + defaultSpy = this.sandbox.spy( jQuery.Animation.prefilters, "0" ); jQuery.Animation.prefilter( prefilter, true ); @@ -133,7 +133,7 @@ QUnit.test( "Animation.prefilter - prefilter return hooks", function( assert ) { assert.equal( arguments[ 2 ], this.opts, "third param opts" ); return ourAnimation; } ), - defaultSpy = sandbox.spy( jQuery.Animation.prefilters, 0 ), + defaultSpy = sandbox.spy( jQuery.Animation.prefilters, "0" ), queueSpy = sandbox.spy( function( next ) { next(); } ), diff --git a/test/unit/attributes.js b/test/unit/attributes.js index 5f2418c7e3..5ace087b96 100644 --- a/test/unit/attributes.js +++ b/test/unit/attributes.js @@ -63,11 +63,11 @@ QUnit.test( "attr(String)", function( assert ) { assert.equal( jQuery( "#text1" ).attr( "type" ), "text", "Check for type attribute" ); assert.equal( jQuery( "#radio1" ).attr( "type" ), "radio", "Check for type attribute" ); assert.equal( jQuery( "#check1" ).attr( "type" ), "checkbox", "Check for type attribute" ); - assert.equal( jQuery( "#simon1" ).attr( "rel" ), "bookmark", "Check for rel attribute" ); + assert.equal( jQuery( "#john1" ).attr( "rel" ), "bookmark", "Check for rel attribute" ); assert.equal( jQuery( "#google" ).attr( "title" ), "Google!", "Check for title attribute" ); - assert.equal( jQuery( "#mark" ).attr( "hreflang" ), "en", "Check for hreflang attribute" ); + assert.equal( jQuery( "#mozilla" ).attr( "hreflang" ), "en", "Check for hreflang attribute" ); assert.equal( jQuery( "#en" ).attr( "lang" ), "en", "Check for lang attribute" ); - assert.equal( jQuery( "#simon" ).attr( "class" ), "blog link", "Check for class attribute" ); + assert.equal( jQuery( "#timmy" ).attr( "class" ), "blog link", "Check for class attribute" ); assert.equal( jQuery( "#name" ).attr( "name" ), "name", "Check for name attribute" ); assert.equal( jQuery( "#text1" ).attr( "name" ), "action", "Check for name attribute" ); assert.ok( jQuery( "#form" ).attr( "action" ).indexOf( "formaction" ) >= 0, "Check for action attribute" ); @@ -239,7 +239,7 @@ QUnit.test( "attr(Hash)", function( assert ) { assert.equal( jQuery( "#text1" ).attr( { "value": function() { - return this[ "id" ]; + return this.id; } } ).attr( "value" ), "text1", "Set attribute to computed value #1" @@ -261,7 +261,7 @@ QUnit.test( "attr(String, Object)", function( assert ) { var $input, $text, $details, attributeNode, commentNode, textNode, obj, - table, td, j, type, + table, td, j, check, thrown, button, $radio, $radios, $svg, div = jQuery( "#qunit-fixture div" ).attr( "foo", "bar" ), i = 0, @@ -304,12 +304,12 @@ QUnit.test( "attr(String, Object)", function( assert ) { assert.equal( $input[ 0 ].selected, true, "Setting selected updates property (verified by native property)" ); $input = jQuery( "#check2" ); - $input.prop( "checked", true ).prop( "checked", false ).attr( "checked", true ); + $input.prop( "checked", true ).prop( "checked", false ).attr( "checked", "checked" ); assert.equal( $input.attr( "checked" ), "checked", "Set checked (verified by .attr)" ); $input.prop( "checked", false ).prop( "checked", true ).attr( "checked", false ); assert.equal( $input.attr( "checked" ), undefined, "Remove checked (verified by .attr)" ); - $input = jQuery( "#text1" ).prop( "readOnly", true ).prop( "readOnly", false ).attr( "readonly", true ); + $input = jQuery( "#text1" ).prop( "readOnly", true ).prop( "readOnly", false ).attr( "readonly", "readonly" ); assert.equal( $input.attr( "readonly" ), "readonly", "Set readonly (verified by .attr)" ); $input.prop( "readOnly", false ).prop( "readOnly", true ).attr( "readonly", false ); assert.equal( $input.attr( "readonly" ), undefined, "Remove readonly (verified by .attr)" ); @@ -318,7 +318,7 @@ QUnit.test( "attr(String, Object)", function( assert ) { assert.equal( $input[ 0 ].checked, true, "Set checked property (verified by native property)" ); assert.equal( $input.prop( "checked" ), true, "Set checked property (verified by .prop)" ); assert.equal( $input.attr( "checked" ), undefined, "Setting checked property doesn't affect checked attribute" ); - $input.attr( "checked", false ).attr( "checked", true ).prop( "checked", false ); + $input.attr( "checked", false ).attr( "checked", "checked" ).prop( "checked", false ); assert.equal( $input[ 0 ].checked, false, "Clear checked property (verified by native property)" ); assert.equal( $input.prop( "checked" ), false, "Clear checked property (verified by .prop)" ); assert.equal( $input.attr( "checked" ), "checked", "Clearing checked property doesn't affect checked attribute" ); @@ -345,8 +345,8 @@ QUnit.test( "attr(String, Object)", function( assert ) { // HTML5 boolean attributes $text = jQuery( "#text1" ).attr( { - "autofocus": true, - "required": true + "autofocus": "autofocus", + "required": "required" } ); assert.equal( $text.attr( "autofocus" ), "autofocus", "Reading autofocus attribute yields 'autofocus'" ); assert.equal( $text.attr( "autofocus", false ).attr( "autofocus" ), undefined, "Setting autofocus to false removes it" ); @@ -354,13 +354,13 @@ QUnit.test( "attr(String, Object)", function( assert ) { assert.equal( $text.attr( "required", false ).attr( "required" ), undefined, "Setting required attribute to false removes it" ); $details = jQuery( "
" ).appendTo( "#qunit-fixture" ); - assert.equal( $details.attr( "open" ), "open", "open attribute presence indicates true" ); + assert.equal( $details.attr( "open" ), "", "open attribute presence indicates true" ); assert.equal( $details.attr( "open", false ).attr( "open" ), undefined, "Setting open attribute to false removes it" ); $text.attr( "data-something", true ); assert.equal( $text.attr( "data-something" ), "true", "Set data attributes" ); assert.equal( $text.data( "something" ), true, "Setting data attributes are not affected by boolean settings" ); - $text.attr( "data-another", false ); + $text.attr( "data-another", "false" ); assert.equal( $text.attr( "data-another" ), "false", "Set data attributes" ); assert.equal( $text.data( "another" ), false, "Setting data attributes are not affected by boolean settings" ); assert.equal( $text.attr( "aria-disabled", false ).attr( "aria-disabled" ), "false", "Setting aria attributes are not affected by boolean settings" ); @@ -394,11 +394,11 @@ QUnit.test( "attr(String, Object)", function( assert ) { table = jQuery( "#table" ).append( "cellcellcellcellcell" ); td = table.find( "td" ).eq( 0 ); td.attr( "rowspan", "2" ); - assert.equal( td[ 0 ][ "rowSpan" ], 2, "Check rowspan is correctly set" ); + assert.equal( td[ 0 ].rowSpan, 2, "Check rowspan is correctly set" ); td.attr( "colspan", "2" ); - assert.equal( td[ 0 ][ "colSpan" ], 2, "Check colspan is correctly set" ); + assert.equal( td[ 0 ].colSpan, 2, "Check colspan is correctly set" ); table.attr( "cellspacing", "2" ); - assert.equal( table[ 0 ][ "cellSpacing" ], "2", "Check cellspacing is correctly set" ); + assert.equal( table[ 0 ].cellSpacing, "2", "Check cellspacing is correctly set" ); assert.equal( jQuery( "#area1" ).attr( "value" ), undefined, "Value attribute is distinct from value property." ); @@ -418,7 +418,6 @@ QUnit.test( "attr(String, Object)", function( assert ) { j.removeAttr( "name" ); // Type - type = jQuery( "#check2" ).attr( "type" ); try { jQuery( "#check2" ).attr( "type", "hidden" ); assert.ok( true, "No exception thrown on input type change" ); @@ -456,6 +455,7 @@ QUnit.test( "attr(String, Object)", function( assert ) { $radio = jQuery( "", { "value": "sup", + // Use uppercase here to ensure the type // attrHook is still used "TYPE": "radio" @@ -479,6 +479,24 @@ QUnit.test( "attr(String, Object)", function( assert ) { assert.equal( jQuery( "#name" ).attr( "nonexisting", undefined ).attr( "nonexisting" ), undefined, ".attr('attribute', undefined) does not create attribute (trac-5571)" ); } ); +QUnit.test( "attr( previously-boolean-attr, non-boolean-value)", function( assert ) { + assert.expect( 3 ); + + var div = jQuery( "
" ).appendTo( "#qunit-fixture" ); + + div.attr( "hidden", "foo" ); + assert.strictEqual( div.attr( "hidden" ), "foo", + "Values not normalized for previously-boolean hidden attribute" ); + + div.attr( "hidden", "until-found" ); + assert.strictEqual( div.attr( "hidden" ), "until-found", + "`until-found` value preserved for hidden attribute" ); + + div.attr( "hiDdeN", "uNtil-fOund" ); + assert.strictEqual( div.attr( "hidden" ), "uNtil-fOund", + "`uNtil-fOund` different casing preserved" ); +} ); + QUnit.test( "attr(non-ASCII)", function( assert ) { assert.expect( 2 ); @@ -701,9 +719,9 @@ QUnit.test( "prop(String, Object) on null/undefined", function( assert ) { $body = jQuery( body ); assert.ok( $body.prop( "nextSibling" ) === null, "Make sure a null expando returns null" ); - body[ "foo" ] = "bar"; + body.foo = "bar"; assert.equal( $body.prop( "foo" ), "bar", "Make sure the expando is preferred over the dom attribute" ); - body[ "foo" ] = undefined; + body.foo = undefined; assert.ok( $body.prop( "foo" ) === undefined, "Make sure the expando is preferred over the dom attribute, even if undefined" ); select = document.createElement( "select" ); @@ -848,7 +866,7 @@ QUnit.test( "removeProp(String)", function( assert ) { obj = {}; assert.strictEqual( - jQuery( "#firstp" ).prop( "nonexisting", "foo" ).removeProp( "nonexisting" )[ 0 ][ "nonexisting" ], + jQuery( "#firstp" ).prop( "nonexisting", "foo" ).removeProp( "nonexisting" )[ 0 ].nonexisting, undefined, "removeprop works correctly on DOM element nodes" ); @@ -856,12 +874,12 @@ QUnit.test( "removeProp(String)", function( assert ) { jQuery.each( [ document, obj ], function( i, ele ) { var $ele = jQuery( ele ); $ele.prop( "nonexisting", "foo" ).removeProp( "nonexisting" ); - assert.strictEqual( ele[ "nonexisting" ], undefined, "removeProp works correctly on non DOM element nodes (bug trac-7500)." ); + assert.strictEqual( ele.nonexisting, undefined, "removeProp works correctly on non DOM element nodes (bug trac-7500)." ); } ); jQuery.each( [ commentNode, textNode, attributeNode ], function( i, ele ) { var $ele = jQuery( ele ); $ele.prop( "nonexisting", "foo" ).removeProp( "nonexisting" ); - assert.strictEqual( ele[ "nonexisting" ], undefined, "removeProp works correctly on non DOM element nodes (bug trac-7500)." ); + assert.strictEqual( ele.nonexisting, undefined, "removeProp works correctly on non DOM element nodes (bug trac-7500)." ); } ); } ); @@ -921,7 +939,7 @@ QUnit.test( "val()", function( assert ) { "Select-one with only option disabled (trac-12584)" ); - if ( jQuery.fn.serialize ) { + if ( includesModule( "serialize" ) ) { checks = jQuery( "" ).appendTo( "#form" ); assert.deepEqual( checks.serialize(), "", "Get unchecked values." ); @@ -1116,7 +1134,7 @@ QUnit.test( "val(select) after form.reset() (Bug trac-2551)", function( assert ) jQuery( "#kkk" ).val( "gf" ); - document[ "kk" ].reset(); + document.kk.reset(); assert.equal( jQuery( "#kkk" )[ 0 ].value, "cf", "Check value of select after form reset." ); assert.equal( jQuery( "#kkk" ).val(), "cf", "Check value of select after form reset." ); @@ -1789,14 +1807,12 @@ QUnit.test( "non-lowercase boolean attribute getters should not crash", function var elem = jQuery( "" ); - jQuery.each( { - checked: "Checked", - required: "requiRed", - autofocus: "AUTOFOCUS" - }, function( lowercased, original ) { + [ + "Checked", "requiRed", "AUTOFOCUS" + ].forEach( function( inconsistentlyCased ) { try { - assert.strictEqual( elem.attr( original ), lowercased, - "The '" + this + "' attribute getter should return the lowercased name" ); + assert.strictEqual( elem.attr( inconsistentlyCased ), "", + "The '" + this + "' attribute getter should return an empty string" ); } catch ( e ) { assert.ok( false, "The '" + this + "' attribute getter threw" ); } @@ -1804,6 +1820,66 @@ QUnit.test( "non-lowercase boolean attribute getters should not crash", function } ); +QUnit.test( "false setter removes non-ARIA attrs (gh-5388)", function( assert ) { + assert.expect( 24 ); + + var elem = jQuery( "