From 6312a6c7e7586398069ea530f51f5d4e3dd27eba Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Go=C5=82e=CC=A8biowski-Owczarek?= Date: Wed, 6 Nov 2024 09:35:00 +0100 Subject: [PATCH] Core: Use `console.info` for deprecations without removals. Also: 1. Group `warnings.md` by this type. 2. Rename `jQuery.migrateWarnings` to `jQuery.migrateMessages`. 3. Rename `jQuery.migrateDeduplicateWarnings` to `jQuery.migrateDeduplicateMessages`. 4. Adding "info" versions of some internal APIs. --- README.md | 10 +-- eslint.config.js | 8 +- src/jquery/ajax.js | 2 +- src/jquery/attributes.js | 2 +- src/jquery/core.js | 32 +++---- src/jquery/css.js | 6 +- src/jquery/deferred.js | 12 ++- src/jquery/effects.js | 2 +- src/jquery/event.js | 14 +-- src/jquery/selector.js | 10 +-- src/main.js | 84 +++++++++++++----- test/data/test-utils.js | 32 +++---- test/data/testinit.js | 10 +-- test/unit/jquery/ajax.js | 10 +-- test/unit/jquery/attributes.js | 10 +-- test/unit/jquery/core.js | 26 +++--- test/unit/jquery/css.js | 20 ++--- test/unit/jquery/deferred.js | 24 ++--- test/unit/jquery/effects.js | 6 +- test/unit/jquery/event.js | 20 ++--- test/unit/jquery/manipulation.js | 8 +- test/unit/jquery/selector.js | 14 +-- test/unit/migrate.js | 28 +++--- warnings.md | 145 ++++++++++++++++--------------- 24 files changed, 289 insertions(+), 246 deletions(-) diff --git a/README.md b/README.md index 34c384c6..78e91633 100644 --- a/README.md +++ b/README.md @@ -48,7 +48,7 @@ The production build is minified and does not generate console warnings. It will ## Debugging -The development version of the plugin displays warnings in the browser console. Older browsers such as IE9 doesn't support the console interface. No messages will be generated unless you include a debugging library such as [Firebug Lite](https://getfirebug.com/firebuglite) before including the jQuery Migrate plugin. Developers can also inspect the `jQuery.migrateWarnings` array to see what error messages have been generated. +The development version of the plugin displays warnings in the browser console. Older browsers such as IE9 doesn't support the console interface. No messages will be generated unless you include a debugging library such as [Firebug Lite](https://getfirebug.com/firebuglite) before including the jQuery Migrate plugin. Developers can also inspect the `jQuery.migrateMessages` array to see what error messages have been generated. All warnings generated by this plugin start with the string "JQMIGRATE". A list of the warnings you may see are in [warnings.md](https://github.com/jquery/jquery-migrate/blob/main/warnings.md). @@ -57,17 +57,17 @@ All warnings generated by this plugin start with the string "JQMIGRATE". A list This plugin adds some properties to the `jQuery` object that can be used to programmatically control and examine its behavior: -`jQuery.migrateWarnings`: This property is an array of string warning messages that have been generated by the code on the page, in the order they were generated. Messages appear in the array only once, even if the condition has occurred multiple times, unless `jQuery.migrateReset()` is called. +`jQuery.migrateMessages`: This property is an array of string warning messages that have been generated by the code on the page, in the order they were generated. Messages appear in the array only once, even if the condition has occurred multiple times, unless `jQuery.migrateReset()` is called. -`jQuery.migrateMute`: Set this property to `true` to prevent console warnings from being generated in the development version. The `jQuery.migrateWarnings` array is still maintained when this property is set, which allows programmatic inspection without console output. +`jQuery.migrateMute`: Set this property to `true` to prevent console warnings from being generated in the development version. The `jQuery.migrateMessages` array is still maintained when this property is set, which allows programmatic inspection without console output. `jQuery.migrateTrace`: Set this property to `false` if you want warnings but do not want stack traces to appear on the console. -`jQuery.migrateReset()`: This method clears the `jQuery.migrateWarnings` array and "forgets" the list of messages that have been seen already. +`jQuery.migrateReset()`: This method clears the `jQuery.migrateMessages` array and "forgets" the list of messages that have been seen already. `jQuery.migrateVersion`: This string property indicates the version of Migrate in use. -`jQuery.migrateDeduplicateWarnings`: By default, Migrate only gives a specific warning once. If you set this property to `false` it will give a warning for every occurrence each time it happens. Note that this can generate a lot of output, for example when a warning occurs in a loop. +`jQuery.migrateDeduplicateMessages`: By default, Migrate only gives a specific warning once. If you set this property to `false` it will give a warning for every occurrence each time it happens. Note that this can generate a lot of output, for example when a warning occurs in a loop. `jQuery.migrateDisablePatches`: Disables patches by their codes. You can find a code for each patch in square brackets in [warnings.md](https://github.com/jquery/jquery-migrate/blob/main/warnings.md). A limited number of warnings doesn't have codes defined and cannot be disabled. These are mostly setup issues like using an incorrect version of jQuery or loading Migrate multiple times. diff --git a/eslint.config.js b/eslint.config.js index ad9e3590..b22b02c6 100644 --- a/eslint.config.js +++ b/eslint.config.js @@ -99,8 +99,8 @@ export default [ QUnit: false, sinon: false, url: false, - expectWarning: false, - expectNoWarning: false, + expectMessage: false, + expectNoMessage: false, compareVersions: false, jQueryVersionSince: false, startIframeTest: false, @@ -132,8 +132,8 @@ export default [ url: true, compareVersions: true, jQueryVersionSince: false, - expectWarning: true, - expectNoWarning: true, + expectMessage: true, + expectNoMessage: true, startIframeTest: true, TestManager: true } diff --git a/src/jquery/ajax.js b/src/jquery/ajax.js index b96336da..fba295ee 100644 --- a/src/jquery/ajax.js +++ b/src/jquery/ajax.js @@ -47,7 +47,7 @@ jQuery.ajaxPrefilter( "+json", function( s, originalSettings, jqXHR ) { // Handle iff the expected data type is "jsonp" or we have a parameter to set if ( jsonProp || s.dataTypes[ 0 ] === "jsonp" ) { - migrateWarn( "jsonp-promotion", "JSON-to-JSONP auto-promotion is deprecated and removed" ); + migrateWarn( "jsonp-promotion", "JSON-to-JSONP auto-promotion is removed" ); // Get callback name, remembering preexisting value associated with it callbackName = s.jsonpCallback = typeof s.jsonpCallback === "function" ? diff --git a/src/jquery/attributes.js b/src/jquery/attributes.js index 6aa2ba43..84af2f34 100644 --- a/src/jquery/attributes.js +++ b/src/jquery/attributes.js @@ -10,7 +10,7 @@ migratePatchFunc( jQuery.fn, "toggleClass", function( state ) { return oldToggleClass.apply( this, arguments ); } - migrateWarn( "toggleClass-bool", "jQuery.fn.toggleClass( boolean ) is deprecated and removed" ); + migrateWarn( "toggleClass-bool", "jQuery.fn.toggleClass( [ boolean ] ) is removed" ); // Toggle entire class name of each element return this.each( function() { diff --git a/src/jquery/core.js b/src/jquery/core.js index 815bedd6..d9cb256a 100644 --- a/src/jquery/core.js +++ b/src/jquery/core.js @@ -1,4 +1,4 @@ -import { migrateWarnProp, migratePatchAndWarnFunc } from "../main.js"; +import { migrateWarnProp, migratePatchAndWarnFunc, migratePatchAndInfoFunc } from "../main.js"; import "../disablePatches.js"; var arr = [], @@ -14,28 +14,28 @@ var arr = [], migratePatchAndWarnFunc( jQuery, "parseJSON", function() { return JSON.parse.apply( null, arguments ); }, "parseJSON", -"jQuery.parseJSON is deprecated and removed; use JSON.parse" ); +"jQuery.parseJSON is removed; use JSON.parse" ); -migratePatchAndWarnFunc( jQuery, "holdReady", jQuery.holdReady, - "holdReady", "jQuery.holdReady is deprecated" ); +migratePatchAndInfoFunc( jQuery, "holdReady", jQuery.holdReady, + "holdReady", "jQuery.holdReady() is deprecated" ); migratePatchAndWarnFunc( jQuery, "unique", jQuery.uniqueSort, - "unique", "jQuery.unique() is deprecated and removed; use jQuery.uniqueSort()" ); + "unique", "jQuery.unique() is removed; use jQuery.uniqueSort()" ); migratePatchAndWarnFunc( jQuery, "trim", function( text ) { return text == null ? "" : ( text + "" ).replace( rtrim, "$1" ); }, "trim", -"jQuery.trim() is deprecated and removed; use String.prototype.trim" ); +"jQuery.trim() is removed; use String.prototype.trim" ); migratePatchAndWarnFunc( jQuery, "nodeName", function( elem, name ) { return elem.nodeName && elem.nodeName.toLowerCase() === name.toLowerCase(); }, "nodeName", -"jQuery.nodeName() is deprecated and removed" ); +"jQuery.nodeName() is removed" ); migratePatchAndWarnFunc( jQuery, "isArray", Array.isArray, "isArray", - "jQuery.isArray() is deprecated and removed; use Array.isArray()" + "jQuery.isArray() is removed; use Array.isArray()" ); migratePatchAndWarnFunc( jQuery, "isNumeric", @@ -52,7 +52,7 @@ migratePatchAndWarnFunc( jQuery, "isNumeric", // subtraction forces infinities to NaN !isNaN( obj - parseFloat( obj ) ); }, "isNumeric", - "jQuery.isNumeric() is deprecated and removed" + "jQuery.isNumeric() is removed" ); // Populate the class2type map @@ -71,30 +71,30 @@ migratePatchAndWarnFunc( jQuery, "type", function( obj ) { class2type[ Object.prototype.toString.call( obj ) ] || "object" : typeof obj; }, "type", -"jQuery.type() is deprecated abd removed" ); +"jQuery.type() is removed" ); migratePatchAndWarnFunc( jQuery, "isFunction", function( obj ) { return typeof obj === "function"; }, "isFunction", -"jQuery.isFunction() is deprecated and removed" ); +"jQuery.isFunction() is removed" ); migratePatchAndWarnFunc( jQuery, "isWindow", function( obj ) { return obj != null && obj === obj.window; }, "isWindow", - "jQuery.isWindow() is deprecated and removed" + "jQuery.isWindow() is removed" ); // Bind a function to a context, optionally partially applying any // arguments. // jQuery.proxy is deprecated to promote standards (specifically Function#bind) // However, it is not slated for removal any time soon -migratePatchAndWarnFunc( jQuery, "proxy", jQuery.proxy, +migratePatchAndInfoFunc( jQuery, "proxy", jQuery.proxy, "proxy", "jQuery.proxy() is deprecated" ); migrateWarnProp( jQuery.fn, "push", push, "push", - "jQuery.fn.push() is deprecated and removed; use .add or convert to an array" ); + "jQuery.fn.push() is removed; use .add() or convert to an array" ); migrateWarnProp( jQuery.fn, "sort", sort, "sort", - "jQuery.fn.sort() is deprecated and removed; convert to an array before sorting" ); + "jQuery.fn.sort() is removed; convert to an array before sorting" ); migrateWarnProp( jQuery.fn, "splice", splice, "splice", - "jQuery.fn.splice() is deprecated and removed; use .slice or .not with .eq" ); + "jQuery.fn.splice() is removed; use .slice() or .not() with .eq()" ); diff --git a/src/jquery/css.js b/src/jquery/css.js index 1e35c5c0..9c7e6bc9 100644 --- a/src/jquery/css.js +++ b/src/jquery/css.js @@ -30,7 +30,7 @@ var origFnCss, internalCssNumber, if ( typeof Proxy !== "undefined" ) { jQuery.cssProps = new Proxy( jQuery.cssProps || {}, { set: function() { - migrateWarn( "cssProps", "jQuery.cssProps is deprecated and removed" ); + migrateWarn( "cssProps", "jQuery.cssProps is removed" ); return Reflect.set.apply( this, arguments ); } } ); @@ -123,8 +123,8 @@ migratePatchFunc( jQuery.fn, "css", function( name, value ) { // internal check. if ( !isAutoPx( camelName ) && !internalCssNumber[ camelName ] ) { migrateWarn( "css-number", - "Auto-appending 'px' to number-typed values is deprecated and removed " + - "for jQuery.fn.css( \"" + name + "\", value )" ); + "Auto-appending 'px' to number-typed values " + + "for jQuery.fn.css( \"" + name + "\", value ) is removed" ); } } diff --git a/src/jquery/deferred.js b/src/jquery/deferred.js index 3164e460..7a1ea3a2 100644 --- a/src/jquery/deferred.js +++ b/src/jquery/deferred.js @@ -1,6 +1,6 @@ import { migratePatchFunc, - migratePatchAndWarnFunc, + migratePatchAndInfoFunc, migrateWarn } from "../main.js"; @@ -14,9 +14,9 @@ migratePatchFunc( jQuery, "Deferred", function( func ) { var deferred = oldDeferred(), promise = deferred.promise(); - migratePatchAndWarnFunc( deferred, "pipe", deferred.pipe, "deferred-pipe", + migratePatchAndInfoFunc( deferred, "pipe", deferred.pipe, "deferred-pipe", "deferred.pipe() is deprecated" ); - migratePatchAndWarnFunc( promise, "pipe", promise.pipe, "deferred-pipe", + migratePatchAndInfoFunc( promise, "pipe", promise.pipe, "deferred-pipe", "deferred.pipe() is deprecated" ); if ( func ) { @@ -40,8 +40,7 @@ Object.defineProperty( jQuery.Deferred, "getStackHook", { get: function() { if ( jQuery.migrateIsPatchEnabled( "deferred-getStackHook" ) ) { migrateWarn( "deferred-getStackHook", - "jQuery.Deferred.getStackHook is deprecated and removed; " + - "use jQuery.Deferred.getErrorHook" ); + "jQuery.Deferred.getStackHook is removed; use jQuery.Deferred.getErrorHook" ); return jQuery.Deferred.getErrorHook; } else { return unpatchedGetStackHookValue; @@ -50,8 +49,7 @@ Object.defineProperty( jQuery.Deferred, "getStackHook", { set: function( newValue ) { if ( jQuery.migrateIsPatchEnabled( "deferred-getStackHook" ) ) { migrateWarn( "deferred-getStackHook", - "jQuery.Deferred.getStackHook is deprecated and removed; " + - "use jQuery.Deferred.getErrorHook" ); + "jQuery.Deferred.getStackHook is removed; use jQuery.Deferred.getErrorHook" ); jQuery.Deferred.getErrorHook = newValue; } else { unpatchedGetStackHookValue = newValue; diff --git a/src/jquery/effects.js b/src/jquery/effects.js index c0ec5df2..33a4c528 100644 --- a/src/jquery/effects.js +++ b/src/jquery/effects.js @@ -5,7 +5,7 @@ import "../disablePatches.js"; if ( jQuery.fx ) { var intervalValue = jQuery.fx.interval, - intervalMsg = "jQuery.fx.interval is deprecated and removed"; + intervalMsg = "jQuery.fx.interval is removed"; // Don't warn if document is hidden, jQuery uses setTimeout (gh-292) Object.defineProperty( jQuery.fx, "interval", { diff --git a/src/jquery/event.js b/src/jquery/event.js index b753dbfb..e13a5085 100644 --- a/src/jquery/event.js +++ b/src/jquery/event.js @@ -1,6 +1,6 @@ import { migrateWarn, - migratePatchAndWarnFunc, + migratePatchAndInfoFunc, migratePatchFunc } from "../main.js"; import "../disablePatches.js"; @@ -26,18 +26,18 @@ jQuery.each( ( "blur focus focusin focusout resize scroll click dblclick " + function( _i, name ) { // Handle event binding - migratePatchAndWarnFunc( jQuery.fn, name, jQuery.fn[ name ], "shorthand-deprecated-v3", + migratePatchAndInfoFunc( jQuery.fn, name, jQuery.fn[ name ], "shorthand-deprecated-v3", "jQuery.fn." + name + "() event shorthand is deprecated" ); } ); -migratePatchAndWarnFunc( jQuery.fn, "bind", jQuery.fn.bind, +migratePatchAndInfoFunc( jQuery.fn, "bind", jQuery.fn.bind, "pre-on-methods", "jQuery.fn.bind() is deprecated" ); -migratePatchAndWarnFunc( jQuery.fn, "unbind", jQuery.fn.unbind, +migratePatchAndInfoFunc( jQuery.fn, "unbind", jQuery.fn.unbind, "pre-on-methods", "jQuery.fn.unbind() is deprecated" ); -migratePatchAndWarnFunc( jQuery.fn, "delegate", jQuery.fn.delegate, +migratePatchAndInfoFunc( jQuery.fn, "delegate", jQuery.fn.delegate, "pre-on-methods", "jQuery.fn.delegate() is deprecated" ); -migratePatchAndWarnFunc( jQuery.fn, "undelegate", jQuery.fn.undelegate, +migratePatchAndInfoFunc( jQuery.fn, "undelegate", jQuery.fn.undelegate, "pre-on-methods", "jQuery.fn.undelegate() is deprecated" ); -migratePatchAndWarnFunc( jQuery.fn, "hover", jQuery.fn.hover, +migratePatchAndInfoFunc( jQuery.fn, "hover", jQuery.fn.hover, "hover", "jQuery.fn.hover() is deprecated" ); diff --git a/src/jquery/selector.js b/src/jquery/selector.js index fdd0f006..e313870c 100644 --- a/src/jquery/selector.js +++ b/src/jquery/selector.js @@ -1,9 +1,9 @@ -import { migratePatchFunc, migrateWarnProp, migrateWarn } from "../main.js"; +import { migratePatchFunc, migrateInfoProp, migrateInfo } from "../main.js"; // Now jQuery.expr.pseudos is the standard incantation -migrateWarnProp( jQuery.expr, "filters", jQuery.expr.pseudos, "expr-pre-pseudos", +migrateInfoProp( jQuery.expr, "filters", jQuery.expr.pseudos, "expr-pre-pseudos", "jQuery.expr.filters is deprecated; use jQuery.expr.pseudos" ); -migrateWarnProp( jQuery.expr, ":", jQuery.expr.pseudos, "expr-pre-pseudos", +migrateInfoProp( jQuery.expr, ":", jQuery.expr.pseudos, "expr-pre-pseudos", "jQuery.expr[':'] is deprecated; use jQuery.expr.pseudos" ); function markFunction( fn ) { @@ -33,7 +33,7 @@ migratePatchFunc( jQuery.expr.filter, "PSEUDO", function( pseudo, argument ) { // But maintain support for old signatures if ( fn.length > 1 ) { - migrateWarn( "legacy-custom-pseudos", + migrateInfo( "legacy-custom-pseudos", "Pseudos with multiple arguments are deprecated; " + "use jQuery.expr.createPseudo()" ); args = [ pseudo, pseudo, "", argument ]; @@ -60,7 +60,7 @@ if ( typeof Proxy !== "undefined" ) { jQuery.expr[ api ] = new Proxy( jQuery.expr[ api ], { set: function( _target, _prop, fn ) { if ( typeof fn === "function" && !fn[ jQuery.expando ] && fn.length > 1 ) { - migrateWarn( "legacy-custom-pseudos", + migrateInfo( "legacy-custom-pseudos", "Pseudos with multiple arguments are deprecated; " + "use jQuery.expr.createPseudo()" ); } diff --git a/src/main.js b/src/main.js index f76cdbd9..849bef56 100644 --- a/src/main.js +++ b/src/main.js @@ -8,7 +8,7 @@ if ( !jQuery || !jQueryVersionSince( "4.0.0" ) || jQueryVersionSince( "5.0.0" ) ) { window.console.log( "JQMIGRATE: jQuery 4.x REQUIRED" ); } -if ( jQuery.migrateWarnings ) { +if ( jQuery.migrateMessages ) { window.console.log( "JQMIGRATE: Migrate plugin loaded multiple times" ); } @@ -19,56 +19,85 @@ window.console.log( "JQMIGRATE: Migrate is installed" + } )(); -var warnedAbout = {}; - -// By default each warning is only reported once. -jQuery.migrateDeduplicateWarnings = true; +var messagesLogged = Object.create( null ); // List of warnings already given; public read only -jQuery.migrateWarnings = []; +jQuery.migrateMessages = []; + +// By default, each warning is only reported once. +if ( jQuery.migrateDeduplicateMessages === undefined ) { + jQuery.migrateDeduplicateMessages = true; +} -// Set to false to disable traces that appear with warnings +// Set to `false` to disable traces that appear with warnings if ( jQuery.migrateTrace === undefined ) { jQuery.migrateTrace = true; } // Forget any warnings we've already given; public jQuery.migrateReset = function() { - warnedAbout = {}; - jQuery.migrateWarnings.length = 0; + messagesLogged = Object.create( null ); + jQuery.migrateMessages.length = 0; }; -export function migrateWarn( code, msg ) { +function migrateMessageInternal( code, msg, consoleMethod ) { var console = window.console; + if ( jQuery.migrateIsPatchEnabled( code ) && - ( !jQuery.migrateDeduplicateWarnings || !warnedAbout[ msg ] ) ) { - warnedAbout[ msg ] = true; - jQuery.migrateWarnings.push( msg + " [" + code + "]" ); - if ( console && console.warn && !jQuery.migrateMute ) { - console.warn( "JQMIGRATE: " + msg ); - if ( jQuery.migrateTrace && console.trace ) { - console.trace(); + ( !jQuery.migrateDeduplicateMessages || !messagesLogged[ msg ] ) ) { + messagesLogged[ msg ] = true; + jQuery.migrateMessages.push( consoleMethod.toUpperCase() + ": " + + msg + " [" + code + "]" ); + + if ( console[ consoleMethod ] && !jQuery.migrateMute ) { + console[ consoleMethod ]( "JQMIGRATE: " + msg ); + + if ( jQuery.migrateTrace ) { + + // Label the trace so that filtering messages in DevTools + // doesn't hide traces. Note that IE ignores the label. + console.trace( "JQMIGRATE: " + msg ); } } } } -export function migrateWarnProp( obj, prop, value, code, msg ) { +export function migrateWarn( code, msg ) { + migrateMessageInternal( code, msg, "warn" ); +} + +export function migrateInfo( code, msg ) { + migrateMessageInternal( code, msg, "info" ); +} + +function migrateMessagePropInternal( + obj, prop, value, code, msg, migrateMessageFn +) { Object.defineProperty( obj, prop, { configurable: true, enumerable: true, get: function() { - migrateWarn( code, msg ); + migrateMessageFn( code, msg ); return value; }, set: function( newValue ) { - migrateWarn( code, msg ); + migrateMessageFn( code, msg ); value = newValue; } } ); } -function migrateWarnFuncInternal( obj, prop, newFunc, code, msg ) { +export function migrateWarnProp( obj, prop, value, code, msg ) { + migrateMessagePropInternal( obj, prop, value, code, msg, migrateWarn ); +} + +export function migrateInfoProp( obj, prop, value, code, msg ) { + migrateMessagePropInternal( obj, prop, value, code, msg, migrateInfo ); +} + +function migrateMessageFuncInternal( + obj, prop, newFunc, code, msg, migrateMessageFn +) { var finalFunc, origFunc = obj[ prop ]; @@ -79,7 +108,7 @@ function migrateWarnFuncInternal( obj, prop, newFunc, code, msg ) { // we just care about the logic choosing the proper implementation // based on whether the patch is disabled or not. if ( msg ) { - migrateWarn( code, msg ); + migrateMessageFn( code, msg ); } // Since patches can be disabled & enabled dynamically, we @@ -98,11 +127,18 @@ export function migratePatchAndWarnFunc( obj, prop, newFunc, code, msg ) { if ( !msg ) { throw new Error( "No warning message provided" ); } - return migrateWarnFuncInternal( obj, prop, newFunc, code, msg ); + return migrateMessageFuncInternal( obj, prop, newFunc, code, msg, migrateWarn ); +} + +export function migratePatchAndInfoFunc( obj, prop, newFunc, code, msg ) { + if ( !msg ) { + throw new Error( "No info message provided" ); + } + return migrateMessageFuncInternal( obj, prop, newFunc, code, msg, migrateInfo ); } export function migratePatchFunc( obj, prop, newFunc, code ) { - return migrateWarnFuncInternal( obj, prop, newFunc, code ); + return migrateMessageFuncInternal( obj, prop, newFunc, code ); } if ( window.document.compatMode === "BackCompat" ) { diff --git a/test/data/test-utils.js b/test/data/test-utils.js index 97efeff3..54f42e22 100644 --- a/test/data/test-utils.js +++ b/test/data/test-utils.js @@ -1,8 +1,8 @@ "use strict"; -/* exported expectWarning, expectNoWarning */ +/* exported expectMessage, expectNoMessage */ -window.expectWarning = function expectWarning( assert, name, expected, fn ) { +window.expectMessage = function expectMessage( assert, name, expected, fn ) { var result; if ( !fn ) { fn = expected; @@ -13,22 +13,22 @@ window.expectWarning = function expectWarning( assert, name, expected, fn ) { function check() { - // Special-case for 0 warnings expected + // Special-case for 0 messages expected if ( expected === 0 ) { - assert.deepEqual( jQuery.migrateWarnings, [], name + ": did not warn" ); + assert.deepEqual( jQuery.migrateMessages, [], name + ": did not message" ); - // Simple numeric equality assertion for warnings matching an explicit count - } else if ( expected && jQuery.migrateWarnings.length === expected ) { - assert.equal( jQuery.migrateWarnings.length, expected, name + ": warned" ); + // Simple numeric equality assertion for messages matching an explicit count + } else if ( expected && jQuery.migrateMessages.length === expected ) { + assert.equal( jQuery.migrateMessages.length, expected, name + ": messaged" ); - // Simple ok assertion when we saw at least one warning and weren't looking for an explict count - } else if ( !expected && jQuery.migrateWarnings.length ) { - assert.ok( true, name + ": warned" ); + // Simple ok assertion when we saw at least one message and weren't looking for an explict count + } else if ( !expected && jQuery.migrateMessages.length ) { + assert.ok( true, name + ": messaged" ); - // Failure; use deepEqual to show the warnings that *were* generated and the expectation + // Failure; use deepEqual to show the messages that *were* generated and the expectation } else { - assert.deepEqual( jQuery.migrateWarnings, - "", name + ": warned" + assert.deepEqual( jQuery.migrateMessages, + "", name + ": messaged" ); } } @@ -45,8 +45,8 @@ window.expectWarning = function expectWarning( assert, name, expected, fn ) { } }; -window.expectNoWarning = function expectNoWarning( assert, name, expected, fn ) { +window.expectNoMessage = function expectNoMessage( assert, name, expected, fn ) { - // Expected is present only for signature compatibility with expectWarning - return expectWarning( assert, name, 0, fn || expected ); + // Expected is present only for signature compatibility with expectMessage + return expectMessage( assert, name, 0, fn || expected ); }; diff --git a/test/data/testinit.js b/test/data/testinit.js index 3551e6c5..5b7f6dbb 100644 --- a/test/data/testinit.js +++ b/test/data/testinit.js @@ -183,20 +183,20 @@ }; QUnit.begin( function() { - originalDeduplicateWarnings = jQuery.migrateDeduplicateWarnings; + originalDeduplicateWarnings = jQuery.migrateDeduplicateMessages; } ); QUnit.testStart( function( details ) { - // If only the first warning is reported, tests using `expectWarning` + // If only the first warning is reported, tests using `expectMessage` // with multiple function calls would pass even if some of them didn't // warn. Because of that, by default don't deduplicate warnings in tests. - if ( details.name !== "jQuery.migrateDeduplicateWarnings" ) { - jQuery.migrateDeduplicateWarnings = false; + if ( details.name !== "jQuery.migrateDeduplicateMessages" ) { + jQuery.migrateDeduplicateMessages = false; } else { // When testing this API, we want to start with its default value. - jQuery.migrateDeduplicateWarnings = originalDeduplicateWarnings; + jQuery.migrateDeduplicateMessages = originalDeduplicateWarnings; } if ( jQuery.migrateDisablePatches ) { diff --git a/test/unit/jquery/ajax.js b/test/unit/jquery/ajax.js index 9122576d..35783ffb 100644 --- a/test/unit/jquery/ajax.js +++ b/test/unit/jquery/ajax.js @@ -21,7 +21,7 @@ QUnit.module( "ajax" ); tests = [ function() { var testName = "dataType: \"json\""; - return expectNoWarning( assert, testName, function() { + return expectNoMessage( assert, testName, function() { return jQuery.ajax( { url: url( "null.json" ), context: { testName: testName }, @@ -38,7 +38,7 @@ QUnit.module( "ajax" ); function() { var testName = "dataType: \"json\", URL callback"; - return expectWarning( assert, testName, forceEnablePatch ? 1 : 0, + return expectMessage( assert, testName, forceEnablePatch ? 1 : 0, function() { return jQuery.ajax( { url: url( "jsonpScript.js?callback=?" ), @@ -56,7 +56,7 @@ QUnit.module( "ajax" ); function() { var testName = "dataType: \"json\", data callback"; - return expectWarning( assert, testName, forceEnablePatch ? 1 : 0, + return expectMessage( assert, testName, forceEnablePatch ? 1 : 0, function() { return jQuery.ajax( { url: url( "jsonpScript.js" ), @@ -75,7 +75,7 @@ QUnit.module( "ajax" ); function() { var testName = "dataType: \"jsonp\", URL callback"; - return expectNoWarning( assert, testName, function() { + return expectNoMessage( assert, testName, function() { return jQuery.ajax( { url: url( "jsonpScript.js?callback=?" ), context: { testName: testName }, @@ -92,7 +92,7 @@ QUnit.module( "ajax" ); function() { var testName = "dataType: \"jsonp\", data callback"; - return expectNoWarning( assert, testName, function() { + return expectNoMessage( assert, testName, function() { return jQuery.ajax( { url: url( "jsonpScript.js" ), context: { testName: testName }, diff --git a/test/unit/jquery/attributes.js b/test/unit/jquery/attributes.js index ffbc9efb..df2c1128 100644 --- a/test/unit/jquery/attributes.js +++ b/test/unit/jquery/attributes.js @@ -6,12 +6,12 @@ QUnit.test( ".toggleClass( boolean )", function( assert ) { var e = jQuery( "
" ).appendTo( "#qunit-fixture" ); - expectWarning( assert, "toggling initially empty class", 1, function() { + expectMessage( assert, "toggling initially empty class", 1, function() { e.toggleClass( true ); assert.equal( e[ 0 ].className, "", "Assert class is empty (data was empty)" ); } ); - expectNoWarning( assert, ".toggleClass( string ) not full className", 1, function() { + expectNoMessage( assert, ".toggleClass( string ) not full className", 1, function() { e.attr( "class", "" ); e.toggleClass( "classy" ); assert.equal( e.attr( "class" ), "classy", "class was toggle-set" ); @@ -19,7 +19,7 @@ QUnit.test( ".toggleClass( boolean )", function( assert ) { assert.equal( e.attr( "class" ), "", "class was toggle-removed" ); } ); - expectWarning( assert, ".toggleClass() save and clear", 1, function() { + expectMessage( assert, ".toggleClass() save and clear", 1, function() { e.addClass( "testD testE" ); assert.ok( e.is( ".testD.testE" ), "Assert class present" ); e.toggleClass(); @@ -28,12 +28,12 @@ QUnit.test( ".toggleClass( boolean )", function( assert ) { // N.B.: Store should have "testD testE" now, next test will assert that } ); - expectWarning( assert, ".toggleClass() restore", 1, function() { + expectMessage( assert, ".toggleClass() restore", 1, function() { e.toggleClass(); assert.ok( e.is( ".testD.testE" ), "Assert class present (restored from data)" ); } ); - expectWarning( assert, ".toggleClass( boolean )", 5, function() { + expectMessage( assert, ".toggleClass( boolean )", 5, function() { e.toggleClass( false ); assert.ok( !e.is( ".testD.testE" ), "Assert class not present" ); e.toggleClass( true ); diff --git a/test/unit/jquery/core.js b/test/unit/jquery/core.js index 28c5221b..2ab857eb 100644 --- a/test/unit/jquery/core.js +++ b/test/unit/jquery/core.js @@ -89,7 +89,7 @@ QUnit.test( "jQuery( '' ) usable on detached elements (#128)", function QUnit.test( "jQuery.parseJSON", function( assert ) { assert.expect( 2 ); - expectWarning( assert, "jQuery.parseJSON", function() { + expectMessage( assert, "jQuery.parseJSON", function() { assert.deepEqual( jQuery.parseJSON( "{\"a\":1}" ), { a: 1 }, @@ -176,7 +176,7 @@ QUnit[ typeof Symbol === "function" ? "test" : "skip" ]( "isNumeric(Symbol)", fu QUnit.test( ".isNumeric (warn)", function( assert ) { assert.expect( 3 ); - expectWarning( assert, "warning on isNumeric (and possibly type)", function() { + expectMessage( assert, "warning on isNumeric (and possibly type)", function() { assert.equal( jQuery.isNumeric( 42 ), true, "isNumeric number" ); assert.equal( jQuery.isNumeric( "nope" ), false, "isNumeric non number" ); } ); @@ -185,7 +185,7 @@ QUnit.test( ".isNumeric (warn)", function( assert ) { QUnit.test( "jQuery.isWindow", function( assert ) { assert.expect( 3 ); - expectWarning( assert, "isWindow", 2, function() { + expectMessage( assert, "isWindow", 2, function() { assert.equal( jQuery.isWindow( [] ), false, "array" ); assert.equal( jQuery.isWindow( window ), true, "window" ); } ); @@ -194,7 +194,7 @@ QUnit.test( "jQuery.isWindow", function( assert ) { QUnit.test( "jQuery.unique", function( assert ) { assert.expect( 2 ); - expectWarning( assert, "jQuery.unique", function() { + expectMessage( assert, "jQuery.unique", function() { var body = jQuery( "body" )[ 0 ], head = jQuery( "head" )[ 0 ]; assert.deepEqual( @@ -207,7 +207,7 @@ QUnit.test( "jQuery.unique", function( assert ) { QUnit.test( "jQuery.holdReady (warn only)", function( assert ) { assert.expect( 1 ); - expectWarning( assert, "jQuery.holdReady", 2, function() { + expectMessage( assert, "jQuery.holdReady", 2, function() { jQuery.holdReady( true ); jQuery.holdReady( false ); } ); @@ -218,7 +218,7 @@ QUnit.test( "jQuery.trim", function( assert ) { var nbsp = String.fromCharCode( 160 ); - expectWarning( assert, "jQuery.trim", 13, function() { + expectMessage( assert, "jQuery.trim", 13, function() { assert.equal( jQuery.trim( "hello " ), "hello", "trailing space" ); assert.equal( jQuery.trim( " hello" ), "hello", "leading space" ); assert.equal( jQuery.trim( " hello " ), "hello", "space on both sides" ); @@ -241,7 +241,7 @@ QUnit.test( "jQuery.trim", function( assert ) { QUnit.test( "jQuery.nodeName", function( assert ) { assert.expect( 2 ); - expectWarning( assert, "jQuery.nodeName", function() { + expectMessage( assert, "jQuery.nodeName", function() { var div = document.createElement( "div" ); assert.equal( jQuery.nodeName( div, "div" ), true, "it's a div" ); @@ -251,7 +251,7 @@ QUnit.test( "jQuery.nodeName", function( assert ) { QUnit.test( "jQuery.isFunction", function( assert ) { assert.expect( 4 ); - expectWarning( assert, "jQuery.isFunction", function() { + expectMessage( assert, "jQuery.isFunction", function() { assert.equal( jQuery.isFunction( function() {} ), true, "function is function" ); assert.equal( jQuery.isFunction( {} ), false, "object not function" ); assert.equal( jQuery.isFunction( 1 ), false, "number not function" ); @@ -301,7 +301,7 @@ QUnit.test( "jQuery.type (warn)", function( assert ) { QUnit.test( "jQuery.isArray", function( assert ) { assert.expect( 4 ); - expectWarning( assert, "isArray", 3, function() { + expectMessage( assert, "isArray", 3, function() { assert.equal( jQuery.isArray( [] ), true, "empty array" ); assert.equal( jQuery.isArray( "" ), false, "empty string" ); assert.equal( jQuery.isArray( jQuery().toArray() ), true, "toArray" ); @@ -319,7 +319,7 @@ TestManager.runIframeTest( "old pre-4.0 jQuery", "core-jquery3.html", QUnit.test( "jQuery.fn.push", function( assert ) { assert.expect( 2 ); - expectWarning( assert, "jQuery.fn.push", 1, function() { + expectMessage( assert, "jQuery.fn.push", 1, function() { var node = jQuery( "
" )[ 0 ], elem = jQuery( "

" ); @@ -333,7 +333,7 @@ QUnit.test( "jQuery.fn.push", function( assert ) { QUnit.test( "jQuery.fn.sort", function( assert ) { assert.expect( 2 ); - expectWarning( assert, "jQuery.fn.sort", 1, function() { + expectMessage( assert, "jQuery.fn.sort", 1, function() { var elem = jQuery( "

" ); elem.sort( function( node1, node2 ) { @@ -356,7 +356,7 @@ QUnit.test( "jQuery.fn.sort", function( assert ) { QUnit.test( "jQuery.fn.splice", function( assert ) { assert.expect( 2 ); - expectWarning( assert, "jQuery.fn.splice", 1, function() { + expectMessage( assert, "jQuery.fn.splice", 1, function() { var elem = jQuery( "

" ); elem.splice( 1, 1, jQuery( "" )[ 0 ], jQuery( "" )[ 0 ] ); @@ -375,7 +375,7 @@ QUnit.test( "jQuery.proxy", function( assert ) { }, thisObject = { foo: "bar", method: test }; - expectWarning( assert, "jQuery.proxy", 7, function() { + expectMessage( assert, "jQuery.proxy", 7, function() { // Make sure normal works test.call( thisObject ); diff --git a/test/unit/jquery/css.js b/test/unit/jquery/css.js index d753284d..d9069da2 100644 --- a/test/unit/jquery/css.js +++ b/test/unit/jquery/css.js @@ -6,11 +6,11 @@ QUnit[ ]( "jQuery.cssProps", function( assert ) { assert.expect( 2 ); - expectWarning( assert, "Write to cssProps", function() { + expectMessage( assert, "Write to cssProps", function() { jQuery.cssProps.devoHat = "awesomeHat"; } ); - expectNoWarning( assert, "Read from cssProps", function() { + expectNoMessage( assert, "Read from cssProps", function() { // eslint-disable-next-line no-unused-expressions jQuery.cssProps.devoHat; // eslint-disable-next-line no-unused-expressions @@ -23,7 +23,7 @@ QUnit[ QUnit.test( "jQuery.css with arrays", function( assert ) { assert.expect( 2 ); - expectNoWarning( assert, "String value direct", function() { + expectNoMessage( assert, "String value direct", function() { var cssValues = jQuery( "
" ) .css( { "z-index": "2", @@ -80,11 +80,11 @@ QUnit[ } ); } - expectWarning( assert, "Number value direct", function() { + expectMessage( assert, "Number value direct", function() { jQuery( "
" ).css( "fake-property", 10 ); } ); - expectWarning( assert, "Number in an object", 1, function() { + expectMessage( assert, "Number in an object", 1, function() { jQuery( "
" ).css( { "width": 14, "height": "10px", @@ -92,11 +92,11 @@ QUnit[ } ); } ); - expectNoWarning( assert, "String value direct", function() { + expectNoMessage( assert, "String value direct", function() { jQuery( "
" ).css( "fake-property", "10px" ); } ); - expectNoWarning( assert, "String in an object", function() { + expectNoMessage( assert, "String in an object", function() { jQuery( "
" ).css( { "width": "14em", "height": "10px", @@ -104,14 +104,14 @@ QUnit[ } ); } ); - expectNoWarning( assert, "Number value (allowlisted props)", function() { + expectNoMessage( assert, "Number value (allowlisted props)", function() { allowlist.forEach( function( prop ) { jQuery( "
" ).css( prop, 1 ); jQuery( "
" ).css( kebabCase( prop ), 1 ); } ); } ); - expectNoWarning( assert, "Props from jQuery.cssNumber", function() { + expectNoMessage( assert, "Props from jQuery.cssNumber", function() { var prop; for ( prop in jQuery.cssNumber ) { jQuery( "
" ).css( prop, 1 ); @@ -122,7 +122,7 @@ QUnit[ // z-index is tested explicitly as raw jQuery 4.0 will not have `jQuery.cssNumber` // so iterating over it won't find anything and we'd like to ensure number values // are not warned against for safe CSS props like z-index (gh-438). - expectNoWarning( assert, "z-index", function() { + expectNoMessage( assert, "z-index", function() { jQuery( "
" ).css( "z-index", 1 ); jQuery( "
" ).css( kebabCase( "zIndex" ), 1 ); } ); diff --git a/test/unit/jquery/deferred.js b/test/unit/jquery/deferred.js index bb2854e0..07eeb0d3 100644 --- a/test/unit/jquery/deferred.js +++ b/test/unit/jquery/deferred.js @@ -39,12 +39,12 @@ QUnit.test( "jQuery.Deferred.getStackHook - getter", function( assert ) { exceptionHookSpy = this.sandbox.spy( jQuery.Deferred, "exceptionHook" ); - expectWarning( assert, "jQuery.Deferred.getStackHook - getter", 1, function() { + expectMessage( assert, "jQuery.Deferred.getStackHook - getter", 1, function() { assert.strictEqual( jQuery.Deferred.getStackHook, jQuery.Deferred.getErrorHook, "getStackHook mirrors getErrorHook (getter)" ); } ); - expectNoWarning( assert, "asyncHook reported in jQuery.Deferred.exceptionHook", function() { + expectNoMessage( assert, "asyncHook reported in jQuery.Deferred.exceptionHook", function() { jQuery .when() .then( function() { @@ -66,7 +66,7 @@ QUnit.test( "jQuery.Deferred.getStackHook - getter, no getErrorHook", function( var done = assert.async(); - expectNoWarning( assert, "No Migrate warning in a regular `then`", function() { + expectNoMessage( assert, "No Migrate warning in a regular `then`", function() { jQuery .when() .then( function() { @@ -83,14 +83,14 @@ QUnit.test( "jQuery.Deferred.getStackHook - setter", function( assert ) { exceptionHookSpy = this.sandbox.spy( jQuery.Deferred, "exceptionHook" ); - expectWarning( assert, "jQuery.Deferred.getStackHook - setter", 1, function() { + expectMessage( assert, "jQuery.Deferred.getStackHook - setter", 1, function() { var mockFn = function() {}; jQuery.Deferred.getStackHook = mockFn; assert.strictEqual( jQuery.Deferred.getErrorHook, mockFn, "getStackHook mirrors getErrorHook (setter)" ); } ); - expectWarning( assert, "asyncHook from jQuery.Deferred.getStackHook reported", + expectMessage( assert, "asyncHook from jQuery.Deferred.getStackHook reported", 1, function() { jQuery.Deferred.getStackHook = function() { @@ -142,12 +142,12 @@ QUnit.test( "jQuery.Deferred.getStackHook - disabled patch, getter", function( a exceptionHookSpy = this.sandbox.spy( jQuery.Deferred, "exceptionHook" ); - expectNoWarning( assert, "jQuery.Deferred.getStackHook - getter", function() { + expectNoMessage( assert, "jQuery.Deferred.getStackHook - getter", function() { assert.strictEqual( jQuery.Deferred.getStackHook, undefined, "getStackHook does not mirror getErrorHook (getter)" ); } ); - expectNoWarning( assert, "asyncHook reported in jQuery.Deferred.exceptionHook", function() { + expectNoMessage( assert, "asyncHook reported in jQuery.Deferred.exceptionHook", function() { jQuery .when() .then( function() { @@ -187,14 +187,14 @@ QUnit.test( "jQuery.Deferred.getStackHook - disabled patch, setter", function( a exceptionHookSpy = this.sandbox.spy( jQuery.Deferred, "exceptionHook" ); - expectNoWarning( assert, "jQuery.Deferred.getStackHook - setter", function() { + expectNoMessage( assert, "jQuery.Deferred.getStackHook - setter", function() { var mockFn = function() {}; jQuery.Deferred.getStackHook = mockFn; assert.strictEqual( jQuery.Deferred.getErrorHook, getErrorHook, "getStackHook does not mirror getErrorHook (setter)" ); } ); - expectNoWarning( assert, "asyncHook from jQuery.Deferred.getStackHook reported", function() { + expectNoMessage( assert, "asyncHook from jQuery.Deferred.getStackHook reported", function() { jQuery.Deferred.getErrorHook = undefined; jQuery.Deferred.getStackHook = function() { @@ -228,7 +228,7 @@ QUnit.test( "jQuery.Deferred.getStackHook - disabled patch, getter + setter inte jQuery.migrateDisablePatches( "deferred-getStackHook" ); - expectNoWarning( assert, "jQuery.Deferred.getStackHook - setter & getter", function() { + expectNoMessage( assert, "jQuery.Deferred.getStackHook - setter & getter", function() { var mockFn = function() {}; assert.strictEqual( jQuery.Deferred.getStackHook, undefined, "getStackHook is `undefined` by default" ); @@ -249,12 +249,12 @@ QUnit.test( ".pipe() warnings", function( assert ) { } // Deferred - expectWarning( assert, "pipe", function() { + expectMessage( assert, "pipe", function() { d.pipe( checkValue ); } ); // Deferred's promise object - expectWarning( assert, "pipe", function() { + expectMessage( assert, "pipe", function() { p.pipe( checkValue ); } ); diff --git a/test/unit/jquery/effects.js b/test/unit/jquery/effects.js index 713b5c20..f158114c 100644 --- a/test/unit/jquery/effects.js +++ b/test/unit/jquery/effects.js @@ -9,12 +9,12 @@ QUnit.test( "jQuery.fx.interval - no warning on animations", function( assert ) var start = assert.async(); - // Can't use expectNoWarning since this is async + // Can't use expectNoMessage since this is async jQuery.migrateReset(); jQuery( "
" ) .appendTo( "#qunit-fixture" ) .animate( { opacity: 0.5 }, 50, function() { - assert.equal( jQuery.migrateWarnings.length, 0, "no warning" ); + assert.equal( jQuery.migrateMessages.length, 0, "no warning" ); start(); } ); } ); @@ -24,7 +24,7 @@ QUnit.test( "jQuery.fx.interval - user change", function( assert ) { assert.expect( 3 ); var oldInterval, - warner = window.requestAnimationFrame ? expectWarning : expectNoWarning; + warner = window.requestAnimationFrame ? expectMessage : expectNoMessage; assert.ok( true, "requestAnimationFrame is " + ( window.requestAnimationFrame ? "present" : "absent" ) ); diff --git a/test/unit/jquery/event.js b/test/unit/jquery/event.js index 65b3a9fb..519c1e01 100644 --- a/test/unit/jquery/event.js +++ b/test/unit/jquery/event.js @@ -6,7 +6,7 @@ QUnit.test( ".bind() and .unbind()", function( assert ) { var $elem = jQuery( "
" ).appendTo( "#qunit-fixture" ); - expectWarning( assert, ".bind()", 1, function() { + expectMessage( assert, ".bind()", 1, function() { $elem .bind( "click", function() { assert.ok( true, "click fired" ); @@ -14,7 +14,7 @@ QUnit.test( ".bind() and .unbind()", function( assert ) { .trigger( "click" ); } ); - expectWarning( assert, ".unbind()", 1, function() { + expectMessage( assert, ".unbind()", 1, function() { $elem .unbind( "click" ) .trigger( "click" ); @@ -28,7 +28,7 @@ QUnit.test( ".delegate() and .undelegate()", function( assert ) { jQuery( "

" ).appendTo( $div ); - expectWarning( assert, ".delegate()", 1, function() { + expectMessage( assert, ".delegate()", 1, function() { $div .delegate( "p", "click", function() { assert.ok( true, "delegated click fired" ); @@ -36,7 +36,7 @@ QUnit.test( ".delegate() and .undelegate()", function( assert ) { .find( "p" ).trigger( "click" ); } ); - expectWarning( assert, ".undelegate()", 1, function() { + expectMessage( assert, ".undelegate()", 1, function() { $div .undelegate( "p", "click" ) .find( "p" ).trigger( "click" ); @@ -49,7 +49,7 @@ QUnit.test( "Event aliases", function( assert ) { var $div = jQuery( "

" ); "scroll click submit keydown".split( " " ).forEach( function( name ) { - expectWarning( assert, "." + name + "()", 2, function() { + expectMessage( assert, "." + name + "()", 2, function() { $div[ name ]( function( event ) { assert.equal( event.type, name, name ); $div.off( event ); @@ -57,14 +57,14 @@ QUnit.test( "Event aliases", function( assert ) { } ); } ); - expectWarning( assert, ".hover() one-arg", 1, function() { + expectMessage( assert, ".hover() one-arg", 1, function() { $div.hover( function( event ) { assert.ok( /mouseenter|mouseleave/.test( event.type ), event.type ); $div.off( event ); } ).trigger( "mouseenter" ).trigger( "mouseleave" ); } ); - expectWarning( assert, ".hover() two-arg", 1, function() { + expectMessage( assert, ".hover() two-arg", 1, function() { $div.hover( function( event ) { assert.equal( "mouseenter", event.type, event.type ); @@ -80,7 +80,7 @@ TestManager.runIframeTest( "Load within a ready handler", "event-lateload.html", function( assert, jQuery ) { assert.expect( 2 ); - assert.equal( jQuery.migrateWarnings.length, 1, "warnings: " + - JSON.stringify( jQuery.migrateWarnings ) ); - assert.ok( /load/.test( jQuery.migrateWarnings[ 0 ] ), "message ok" ); + assert.equal( jQuery.migrateMessages.length, 1, "warnings: " + + JSON.stringify( jQuery.migrateMessages ) ); + assert.ok( /load/.test( jQuery.migrateMessages[ 0 ] ), "message ok" ); } ); diff --git a/test/unit/jquery/manipulation.js b/test/unit/jquery/manipulation.js index 4e2fd5a6..7ec55bce 100644 --- a/test/unit/jquery/manipulation.js +++ b/test/unit/jquery/manipulation.js @@ -12,26 +12,26 @@ QUnit.test( "Improperly closed elements", function( assert ) { jQuery.migrateEnablePatches( "self-closed-tags" ); - expectWarning( assert, "Elements not self-closable nested wrong", 4, function() { + expectMessage( assert, "Elements not self-closable nested wrong", 4, function() { jQuery( "

" ); jQuery( "

" ); jQuery( "
" ).append( "

" ); jQuery( "" ); jQuery( "
" ).append( "" ); jQuery( "" ); } ); - expectNoWarning( assert, "Elements not self-closable but tolerable", function() { + expectNoMessage( assert, "Elements not self-closable but tolerable", function() { jQuery( "
" ); jQuery( "

" ); jQuery( "

" ).append( "" ); } ); - expectNoWarning( assert, "Bare elements", function() { + expectNoMessage( assert, "Bare elements", function() { jQuery( "

" ).append( "" ); jQuery( "" ); jQuery( "" ); diff --git a/test/unit/jquery/selector.js b/test/unit/jquery/selector.js index ab2fe941..886d6432 100644 --- a/test/unit/jquery/selector.js +++ b/test/unit/jquery/selector.js @@ -60,19 +60,19 @@ function testSelector( assert, message, selector, expectedIds ) { QUnit.test( "jQuery.expr.pseudos aliases", function( assert ) { assert.expect( 7 ); - expectWarning( assert, "jQuery.expr.filters", function() { + expectMessage( assert, "jQuery.expr.filters", function() { jQuery.expr.filters.mazda = function( elem ) { return elem.style.zoom === "3"; }; } ); - expectWarning( assert, "jQuery.expr[':']", function() { + expectMessage( assert, "jQuery.expr[':']", function() { jQuery.expr[ ":" ].marginal = function( elem ) { return parseInt( elem.style.marginLeftWidth ) > 20; }; } ); - expectNoWarning( assert, "jQuery.expr.pseudos", function() { + expectNoMessage( assert, "jQuery.expr.pseudos", function() { var fixture = jQuery( "#qunit-fixture" ).prepend( "

hello

" ); assert.ok( jQuery.expr.pseudos.mazda, "filters assigned" ); @@ -88,7 +88,7 @@ QUnit.test( "jQuery.expr.pseudos aliases", function( assert ) { QUnit.test( "custom pseudos", function( assert ) { assert.expect( 7 ); - expectNoWarning( assert, "custom pseudos", function() { + expectNoMessage( assert, "custom pseudos", function() { try { jQuery.expr.pseudos.foundation = jQuery.expr.pseudos.root; assert.deepEqual( @@ -193,7 +193,7 @@ QUnit.test( "backwards-compatible custom pseudos", function( assert ) { assert.expect( 7 ); var expectWarningWithProxy = typeof Proxy !== "undefined" ? - expectWarning : + expectMessage : function( _assert, _title, fn ) { fn(); assert.ok( true, "No Proxy => warnings not expected" ); @@ -208,7 +208,7 @@ QUnit.test( "backwards-compatible custom pseudos", function( assert ) { .indexOf( ( match[ 3 ] || "" ).toLowerCase() ) > -1; }; } ); - expectWarning( assert, "Custom element filter with argument - getter", function() { + expectMessage( assert, "Custom element filter with argument - getter", function() { testSelector( assert, "Custom element filter with argument", @@ -227,7 +227,7 @@ QUnit.test( "backwards-compatible custom pseudos", function( assert ) { return elements.slice( 0, count ); }; } ); - expectWarning( assert, "Custom setFilter pseudo - getter", function() { + expectMessage( assert, "Custom setFilter pseudo - getter", function() { // Using TAG as the first token here forces this setMatcher into a fail state // Where the descendent combinator was lost diff --git a/test/unit/migrate.js b/test/unit/migrate.js index c557794a..e3c2ffb7 100644 --- a/test/unit/migrate.js +++ b/test/unit/migrate.js @@ -24,25 +24,25 @@ QUnit.test( "compareVersions and jQueryVersionSince", function( assert ) { assert.equal( jQueryVersionSince( jQuery.fn.jquery ), true, "since - equal" ); } ); -QUnit.test( "jQuery.migrateDeduplicateWarnings", function( assert ) { +QUnit.test( "jQuery.migrateDeduplicateMessages", function( assert ) { assert.expect( 3 ); - var origValue = jQuery.migrateDeduplicateWarnings; + var origValue = jQuery.migrateDeduplicateMessages; assert.strictEqual( origValue, true, "true by default" ); - jQuery.migrateDeduplicateWarnings = true; - expectWarning( assert, "jQuery.migrateDeduplicateWarnings === true", 1, function() { + jQuery.migrateDeduplicateMessages = true; + expectMessage( assert, "jQuery.migrateDeduplicateMessages === true", 1, function() { jQuery.trim( " a " ); jQuery.trim( "a" ); } ); - jQuery.migrateDeduplicateWarnings = false; - expectWarning( assert, "jQuery.migrateDeduplicateWarnings === false", 2, function() { + jQuery.migrateDeduplicateMessages = false; + expectMessage( assert, "jQuery.migrateDeduplicateMessages === false", 2, function() { jQuery.trim( " a " ); jQuery.trim( "a" ); } ); - jQuery.migrateDeduplicateWarnings = origValue; + jQuery.migrateDeduplicateMessages = origValue; } ); QUnit.test( "disabling/enabling patches", function( assert ) { @@ -63,13 +63,13 @@ QUnit.test( "disabling/enabling patches", function( assert ) { assert.strictEqual( jQuery.migrateIsPatchEnabled( "shorthand-deprecated-v3" ), true, "patch enabled by default (shorthand-deprecated-v3)" ); - expectWarning( assert, "pre-on-methods (default)", function() { + expectMessage( assert, "pre-on-methods (default)", function() { jQuery().bind(); } ); - expectWarning( assert, "proxy (default)", function() { + expectMessage( assert, "proxy (default)", function() { jQuery.proxy( jQuery.noop ); } ); - expectWarning( assert, "shorthand-deprecated-v3 (default)", function() { + expectMessage( assert, "shorthand-deprecated-v3 (default)", function() { jQuery().click(); } ); @@ -81,13 +81,13 @@ QUnit.test( "disabling/enabling patches", function( assert ) { assert.strictEqual( jQuery.migrateIsPatchEnabled( "shorthand-deprecated-v3" ), true, "patch still enabled (shorthand-deprecated-v3)" ); - expectNoWarning( assert, "pre-on-methods (default)", function() { + expectNoMessage( assert, "pre-on-methods (default)", function() { jQuery().bind(); } ); - expectNoWarning( assert, "proxy (default)", function() { + expectNoMessage( assert, "proxy (default)", function() { jQuery.proxy( jQuery.noop ); } ); - expectWarning( assert, "shorthand-deprecated-v3 (default)", function() { + expectMessage( assert, "shorthand-deprecated-v3 (default)", function() { jQuery().click(); } ); @@ -95,7 +95,7 @@ QUnit.test( "disabling/enabling patches", function( assert ) { assert.strictEqual( jQuery.migrateIsPatchEnabled( "shorthand-deprecated-v3" ), false, "patch disabled (shorthand-deprecated-v3)" ); - expectNoWarning( assert, "shorthand-deprecated-v3 (disabled)", function() { + expectNoMessage( assert, "shorthand-deprecated-v3 (disabled)", function() { jQuery().click(); } ); } ); diff --git a/warnings.md b/warnings.md index f3fb23b9..a90c3cd8 100644 --- a/warnings.md +++ b/warnings.md @@ -8,7 +8,10 @@ To allow developers to identify and fix compatibility issues when migrating olde The production (compressed) version of the plugin does not generate warnings. To continue using jQuery code that has compatibility issues without making any changes and without console messages, simply include the production version in the file rather than the development version. See the [README](README.md) for download instructions. -All messages generated by this plugin start with the text "JQMIGRATE" for easy identification. The warning messages, causes, and remediation instructions are listed below. Items listed as **deprecated and removed** must be changed before the code will work properly without the Migrate plugin. Items listed as only **deprecated** are still supported by the current version but no longer considered a good practice, and may be removed in the future. +All messages generated by this plugin start with the text "JQMIGRATE" for easy identification. The warning messages, causes, and remediation instructions are listed below. Items listed as **removed** - printed by `console.warn` must be changed before the code will work properly without the Migrate plugin. Items listed as only **deprecated** - printed by `console.info` - are still supported by the current version but no longer considered a good practice, and may be removed in the future. + + +## General logs & warnings ### JQMIGRATE: Migrate is installed, version X ### JQMIGRATE: Migrate is installed with logging active, version X @@ -33,139 +36,96 @@ This is _not_ a warning, but a console log message the plugin shows when it firs **Solution:** Put a [valid doctype](http://www.w3.org/QA/2002/04/valid-dtd-list.html) in the document and ensure that the document is rendering in standards mode. The simplest valid doctype is the HTML5 one, which we highly recommend: `` . The jQuery Migrate plugin does not attempt to fix issues related to quirks mode. -### \[deferred-pipe\] JQMIGRATE: deferred.pipe() is deprecated - -**Cause**: The `.pipe()` method on a `jQuery.Deferred` object was deprecated as of jQuery 1.8, when the `.then()` method was changed to perform the same function. +### \[load-after-event\] JQMIGRATE: jQuery(window).on('load'...) called after load event occurred -**Solution**: In most cases it is sufficient to change all occurrences of `.pipe()` to `.then()`. Ensure that you aren't relying on context/state propagation (e.g., using `this`) or synchronous callback invocation, which were dropped from `.then()` for Promises/A+ interoperability as of jQuery 3.0. +**Cause:** The calling code has attempted to attach a `load` event to `window` after the page has already loaded. That means the handler will never run and so is probably not what the caller intended. This can occur when the event attachment is made too late, for example, in a jQuery ready handler. It can also occur when a file is loaded dynamically with jQuery after the page has loaded, for example using the `$.getScript()` method. -### \[fx-interval\] JQMIGRATE: jQuery.fx.interval is deprecated and removed +**Solution:** If a function `fn` does not actually depend on all page assets being fully loaded, switch to a ready handler `$( fn )` which runs earlier and will always run `fn` even if the script that contains the code loads long after the page has fully loaded. If `fn` actually does depend on the script being fully loaded, check `document.readyState`. If the value is `"complete"` run the function immediately, otherwise use `$(window).on( "load", fn )`. -**Cause**: As of jQuery 4.0, the `jQuery.fx.interval` property is no longer consulted and it's no longer possible to change the animation interval on browsers that do not support the `window.requestAnimationFrame()` method. -**Solution**: Find and remove code that changes or uses `jQuery.fx.interval`. If the value is being used by code in your page or a plugin, the code may be making assumptions that are no longer valid. The default value of `jQuery.fx.interval` is `13` (milliseconds), which could be used instead of accessing this property. +## Removed APIs -### \[pre-on-methods\] JQMIGRATE: jQuery.fn.bind() is deprecated -### \[pre-on-methods\] JQMIGRATE: jQuery.fn.unbind() is deprecated -### \[pre-on-methods\] JQMIGRATE: jQuery.fn.delegate() is deprecated -### \[pre-on-methods\] JQMIGRATE: jQuery.fn.undelegate() is deprecated +### \[fx-interval\] JQMIGRATE: jQuery.fx.interval is removed -**Cause:**: These event binding methods have been deprecated in favor of the `.on()` and `.off()` methods which can handle both delegated and direct event binding. Although the older methods are still present in jQuery 4.x, they may be removed as early as the next major-version update. +**Cause**: As of jQuery 4.0, the `jQuery.fx.interval` property is no longer consulted and it's no longer possible to change the animation interval on browsers that do not support the `window.requestAnimationFrame()` method. -**Solution**: Change the method call to use `.on()` or `.off()`, the documentation for the old methods include specific instructions. In general, the `.bind()` and `.unbind()` methods can be renamed directly to `.on()` and `.off()` respectively since the argument orders are identical. +**Solution**: Find and remove code that changes or uses `jQuery.fx.interval`. If the value is being used by code in your page or a plugin, the code may be making assumptions that are no longer valid. The default value of `jQuery.fx.interval` is `13` (milliseconds), which could be used instead of accessing this property. -### \[parseJSON\] JQMIGRATE: jQuery.parseJSON is deprecated and removed; use JSON.parse +### \[parseJSON\] jQuery.parseJSON is removed; use JSON.parse **Cause**: The `jQuery.parseJSON` method in recent jQuery is identical to the native `JSON.parse`. As of jQuery 4.0 `jQuery.parseJSON` is removed. **Solution**: Replace any use of `jQuery.parseJSON` with `JSON.parse`. -### \[isNumeric\] JQMIGRATE: jQuery.isNumeric() is deprecated and removed +### \[isNumeric\] JQMIGRATE: jQuery.isNumeric() is removed **Cause**: This method was used by jQuery to determine if certain string arguments could be converted to numbers, but the name led people to apply their own interpretations to what the method means. As a result, it often doesn't meet the needs of specific cases. For example, a 25-character string of only digits is technically a valid number, but JavaScript cannot represent it accurately. The string `"0x251D"` is a valid hexadecimal number but may not be acceptable numeric input to a web form. **Solution**: Use a test for being numeric that makes sense for the specific situation. For example, instead of `jQuery.isNumeric(string)`, use `isNan(parseFloat(string))` if a floating point number is expected, or `string.test(/^[0-9]{1,8}$/)` if a sequence of 1 to 8 digits is expected. -### \[type\] JQMIGRATE: jQuery.type() is deprecated and removed +### \[type\] JQMIGRATE: jQuery.type() is removed **Cause**: This method returns a string that indicates the type of the argument, for example `"number"` or `"function"`. However, as the JavaScript language evolves this method has become problematic because new language constructs might require this function to either return a new string (potentially breaking existing code) or somehow map new constructs into existing strings (again, potentially breaking existing code). Examples of new recent JavaScript features include asynchronous functions, class constructors, `Symbol`s, or functions that act as iterators. **Solution**: Review code that uses `jQuery.type()` and use a type check that is appropriate for the situation. For example. if the code expects a plain function, check for `typeof arg === "function"`. -### \[push\] JQMIGRATE: jQuery.fn.push() is deprecated and removed; use .add or convert to an array -### \[sort\] JQMIGRATE: jQuery.fn.sort() is deprecated and removed; convert to an array before sorting -### \[splice\] JQMIGRATE: jQuery.fn.splice() is deprecated and removed; use .slice or .not with .eq +### \[push\] JQMIGRATE: jQuery.fn.push() is removed; use .add or convert to an array +### \[sort\] JQMIGRATE: jQuery.fn.sort() is removed; convert to an array before sorting +### \[splice\] JQMIGRATE: jQuery.fn.splice() is removed; use .slice() or .not() with .eq() **Cause**: jQuery used to add the Array `push`, `sort` & `splice` methods to the jQuery prototype. They behaved differently to other jQuery APIs - they modify the jQuery collections in place, they don't play nice with APIs like `.end()`, they were also never documented. **Solution**: Replace `.push( node )` with `.add( node )`, `.splice( index )` with `.not( elem.eq( index ) )`. In more complex cases, call `.toArray()` first, manipulate the resulting array and convert back to the jQuery object by passing the resulting array to `$()`. -### \[unique\] JQMIGRATE: jQuery.unique() is deprecated and removed; use jQuery.uniqueSort() +### \[unique\] JQMIGRATE: jQuery.unique() is removed; use jQuery.uniqueSort() **Cause**: The fact that `jQuery.unique` sorted its results in DOM order was surprising to many who did not read the documentation carefully. As of jQuery 3.0 this function is being renamed to make it clear. As of jQuery 4.0, the old alias no longer exists. **Solution**: Replace all uses of `jQuery.unique` with `jQuery.uniqueSort` which is the same function with a better name. -### \[expr-pre-pseudos\] JQMIGRATE: jQuery.expr\[':'\] is deprecated; use jQuery.expr.pseudos -### \[expr-pre-pseudos\] JQMIGRATE: jQuery.expr.filters is deprecated; use jQuery.expr.pseudos - -**Cause:** The standard way to add new custom selectors through jQuery is `jQuery.expr.pseudos`. These two other aliases are deprecated, although they still work as of jQuery 3.0. - -**Solution:** Rename any of the older usage to `jQuery.expr.pseudos`. The functionality is identical. - -### \[toggleClass-bool\] JQMIGRATE: jQuery.fn.toggleClass( \[ boolean \] ) is deprecated and removed +### \[toggleClass-bool\] JQMIGRATE: `jQuery.fn.toggleClass( \[ boolean \] ) is removed` **Cause:** Calling `.toggleClass()` with no arguments, or with a single Boolean `true` or `false` argument, has been deprecated. Its behavior was poorly documented, but essentially the method saved away the current `class` value in a data item when the class was removed and restored the saved value when it was toggled back. If you do not believe you are specificially trying to use this form of the method, it is possible you are accidentally doing so via an inadvertent undefined value, as `.toggleClass( undefined )` toggles all classes. **Solution:** If this functionality is still needed, save the current full `.attr( "class" )` value in a data item and restore it when required. -### \[load-after-event\] JQMIGRATE: jQuery(window).on('load'...) called after load event occurred - -**Cause:** The calling code has attempted to attach a `load` event to `window` after the page has already loaded. That means the handler will never run and so is probably not what the caller intended. This can occur when the event attachment is made too late, for example, in a jQuery ready handler. It can also occur when a file is loaded dynamically with jQuery after the page has loaded, for example using the `$.getScript()` method. - -**Solution:** If a function `fn` does not actually depend on all page assets being fully loaded, switch to a ready handler `$( fn )` which runs earlier and will always run `fn` even if the script that contains the code loads long after the page has fully loaded. If `fn` actually does depend on the script being fully loaded, check `document.readyState`. If the value is `"complete"` run the function immediately, otherwise use `$(window).on( "load", fn )`. - -### \[holdReady\] JQMIGRATE: jQuery.holdReady() is deprecated - -**Cause:** The `jQuery.holdReady()` method has been deprecated due to its detrimental effect on the global performance of the page. This method can prevent all the code on the page from initializing for extended lengths of time. - -**Solution:** Rewrite the page so that it does not require all jQuery ready handlers to be delayed. This might be accomplished, for example, by late-loading only the code that requires the delay when it is safe to run. Due to the complexity of this method, jQuery Migrate does not attempt to fill the functionality. If the underlying version of jQuery used with jQuery Migrate no longer contains `jQuery.holdReady()` the code will fail shortly after this warning appears. - -### \[isFunction\] JQMIGRATE: jQuery.isFunction() is deprecated and removed +### \[isFunction\] JQMIGRATE: jQuery.isFunction() is removed **Cause:** This method returns `true` if its argument is thought to be a function. It was created to work around bugs in `typeof` implementations in legacy browsers. **Solution:** Replace any use of `jQuery.isFunction( x )` with `typeof x === "function"`. -### \[isWindow\] JQMIGRATE: jQuery.isWindow() is deprecated and removed +### \[isWindow\] jQuery.isWindow() is removed **Cause:** This method returns `true` if its argument is thought to be a `window` element. It was created for internal use and is not a reliable way of detecting `window` for public needs. **Solution:** Remove any use of `jQuery.isWindow()` from code. If it is truly needed it can be replaced with a check for `obj != null && obj === obj.window` which was the test used inside this method. -### \[proxy\] JQMIGRATE: jQuery.proxy() is deprecated - -**Cause:** This method, while having some differences, is similar to native `Function.prototype.bind` so it got deprecated to promote usage of native `bind`. - -**Solution:** Replace any calls to `jQuery.proxy(fn, context, param1, param2)` with `fn.bind(context, param1, param2)`. Be careful if you use a proxied function for event handling as jQuery matches a proxied function to its original when removing event handlers which is not the case when native `bind` is used. - -### \[shorthand-deprecated-v3\] JQMIGRATE: jQuery.fn.click() event shorthand is deprecated - -**Cause:** The `.on()` and `.trigger()` methods can set an event handler or generate an event for any event type, and should be used instead of the shortcut methods. This message also applies to the other event shorthands, including: blur, focus, focusin, focusout, resize, scroll, dblclick, mousedown, mouseup, mousemove, mouseover, mouseout, mouseenter, mouseleave, change, select, submit, keydown, keypress, keyup, and contextmenu. - -**Solution:** Instead of `.click(fn)` use `.on("click", fn)`. Instead of `.click()` use `.trigger("click")`. - -### \[hover\] JQMIGRATE: jQuery.fn.hover() is deprecated - -**Cause:** The `.hover()` method is a shorthand for the use of the `mouseover`/`mouseout` events. It is often a poor user interface choice because it does not allow for any small amounts of delay between when the mouse enters or exits an area and when the event fires. This can make it quite difficult to use with UI widgets such as drop-down menus. For more information on the problems of hovering, see the [hoverIntent plugin](http://cherne.net/brian/resources/jquery.hoverIntent.html). - -**Solution:** Review uses of `.hover()` to determine if they are appropriate, and consider use of plugins such as `hoverIntent` as an alternative. The direct replacement for `.hover(fn1, fn2)`, is `.on("mouseenter", fn1).on("mouseleave", fn2)`. - -### \[nodeName\] JQMIGRATE: jQuery.nodeName() is deprecated +### \[nodeName\] JQMIGRATE: jQuery.nodeName() is removed **Cause:** This public but never-documented method has been deprecated as of jQuery 3.2.0 and removed as of jQuery 4.0.0. **Solution:** Replace calls such as `jQuery.nodeName( elem, "div" )` with a test such as `elem.nodeName.toLowerCase() === "div"`. -### \[cssProps\] JQMIGRATE: jQuery.cssProps is deprecated and removed +### \[cssProps\] JQMIGRATE: jQuery.cssProps is removed **Cause:** The `jQuery.cssProps` property is a public but undocumented object that allows CSS properties with one name to be mapped into another name. It was used for legacy browsers like IE8 that used non-standard names. This object is no longer used inside jQuery since all supported browsers now use the standard CSS property names. **Solution:** Remove any uses of `jQuery.cssProps` in application code. -### \[isArray\] JQMIGRATE: jQuery.isArray() is deprecated and removed; use Array.isArray() +### \[isArray\] JQMIGRATE: jQuery.isArray() is removed; use Array.isArray() **Cause:** Older versions of JavaScript made it difficult to determine if a particular object was a true Array, so jQuery provided a cross-browser function to do the work. The browsers supported by jQuery 3.0 all provide a standard method for this purpose. **Solution:** Replace any calls to `jQuery.isArray` with `Array.isArray`. -### \[trim\] JQMIGRATE: jQuery.trim() is deprecated; use String.prototype.trim +### \[trim\] JQMIGRATE: jQuery.trim() is removed; use String.prototype.trim **Cause:** Older versions of IE & Android Browser didn't implement a method to `trim` strings so jQuery provided a cross-browser implementation. The browsers supported by jQuery 3.0 all provide a standard method for this purpose. **Solution:** Replace any calls to `jQuery.trim( text )` with `text.trim()` if you know `text` is a string; otherwise, you can replace it with `String.prototype.trim.call( text == null ? "" : text )`. -### \[css-number\] JQMIGRATE: Auto-appending 'px' to number-typed values is deprecated and removed for jQuery.fn.css( _(property name)_, value ) +### \[css-number\] JQMIGRATE: Auto-appending 'px' to number-typed values for jQuery.fn.css( _(property name)_, value ) is removed **Cause:** In past versions, when a number-typed value was passed to `.css()` jQuery converted it to a string and added `"px"` to the end. As the CSS standard has evolved, an increasingly large set of CSS properties now accept values that are unitless numbers, where this behavior is incorrect. It has become impractical to manage these exceptions in the `jQuery.cssNumber` object. In addition, some CSS properties like `line-height` can accept both a bare number `2` or a pixel value `2px`. jQuery cannot know the correct way to interpret `$.css("line-height", 2)` and currently treats it as `"2px"`. @@ -177,14 +137,63 @@ This is _not_ a warning, but a console log message the plugin shows when it firs **Solution:** Search for the reported HTML strings and edit the tags to close them explicitly. In some cases the strings passed to jQuery may be created inside the program and thus not searchable. Migrate warning messages include a stack trace that can be used to find the location of the usage in the code. -### \[jsonp-promotion\] JQMIGRATE: JSON-to-JSONP auto-promotion is deprecated and removed +### \[jsonp-promotion\] JQMIGRATE: JSON-to-JSONP auto-promotion is removed **Cause:** `jQuery.ajax` calls with `dataType: 'json'` with a provided callback are automatically converted by jQuery to JSONP requests unless the options also specify `jsonp: false`. Auto-promoting JSON requests to JSONP introduces a security risk as the developer may be unaware they're not just downloading data but executing code from a remote domain. This auto-promoting behavior is deprecated and will be removed in jQuery 4.0.0. **Solution:** To trigger a JSONP request, specify the `dataType: "jsonp"` option. -### \[deferred-getStackHook\] JQMIGRATE: jQuery.Deferred.getStackHook is deprecated and removed; use jQuery.Deferred.getErrorHook +### \[deferred-getStackHook\] JQMIGRATE: jQuery.Deferred.getStackHook is removed; use jQuery.Deferred.getErrorHook **Cause:** `jQuery.Deferred.getStackHook` was originally created to pass the stack trace from before an async barrier to report when a user error (like calling a non-existing function) causes a promise to be rejected. However, passing a stack trace doesn't take source maps into account, so we started advising to pass the whole error object. To make it clearer, we also renamed the API to `jQuery.Deferred.getErrorHook`. The legacy alias will be removed in jQuery 4.0.0 **Solution:** Rename all usage of `jQuery.Deferred.getStackHook` to `jQuery.Deferred.getErrorHook`. If you previously assigned a function returning an error stack to `jQuery.Deferred.getStackHook` or `jQuery.Deferred.getErrorHook`, change it to return a full error object. + + +## Deprecated APIs + +### \[deferred-pipe\] JQMIGRATE: deferred.pipe() is deprecated + +**Cause**: The `.pipe()` method on a `jQuery.Deferred` object was deprecated as of jQuery 1.8, when the `.then()` method was changed to perform the same function. + +**Solution**: In most cases it is sufficient to change all occurrences of `.pipe()` to `.then()`. Ensure that you aren't relying on context/state propagation (e.g., using `this`) or synchronous callback invocation, which were dropped from `.then()` for Promises/A+ interoperability as of jQuery 3.0. + +### \[pre-on-methods\] JQMIGRATE: jQuery.fn.bind() is deprecated +### \[pre-on-methods\] JQMIGRATE: jQuery.fn.unbind() is deprecated +### \[pre-on-methods\] JQMIGRATE: jQuery.fn.delegate() is deprecated +### \[pre-on-methods\] JQMIGRATE: jQuery.fn.undelegate() is deprecated + +**Cause:**: These event binding methods have been deprecated in favor of the `.on()` and `.off()` methods which can handle both delegated and direct event binding. Although the older methods are still present in jQuery 4.x, they may be removed as early as the next major-version update. + +**Solution**: Change the method call to use `.on()` or `.off()`, the documentation for the old methods include specific instructions. In general, the `.bind()` and `.unbind()` methods can be renamed directly to `.on()` and `.off()` respectively since the argument orders are identical. + +### \[expr-pre-pseudos\] JQMIGRATE: jQuery.expr\[':'\] is deprecated; use jQuery.expr.pseudos +### \[expr-pre-pseudos\] JQMIGRATE: jQuery.expr.filters is deprecated; use jQuery.expr.pseudos + +**Cause:** The standard way to add new custom selectors through jQuery is `jQuery.expr.pseudos`. These two other aliases are deprecated, although they still work as of jQuery 3.0. + +**Solution:** Rename any of the older usage to `jQuery.expr.pseudos`. The functionality is identical. + +### \[holdReady\] JQMIGRATE: jQuery.holdReady() is deprecated + +**Cause:** The `jQuery.holdReady()` method has been deprecated due to its detrimental effect on the global performance of the page. This method can prevent all the code on the page from initializing for extended lengths of time. + +**Solution:** Rewrite the page so that it does not require all jQuery ready handlers to be delayed. This might be accomplished, for example, by late-loading only the code that requires the delay when it is safe to run. Due to the complexity of this method, jQuery Migrate does not attempt to fill the functionality. If the underlying version of jQuery used with jQuery Migrate no longer contains `jQuery.holdReady()` the code will fail shortly after this warning appears. + +### \[proxy\] JQMIGRATE: jQuery.proxy() is deprecated + +**Cause:** This method, while having some differences, is similar to native `Function.prototype.bind` so it got deprecated to promote usage of native `bind`. + +**Solution:** Replace any calls to `jQuery.proxy(fn, context, param1, param2)` with `fn.bind(context, param1, param2)`. Be careful if you use a proxied function for event handling as jQuery matches a proxied function to its original when removing event handlers which is not the case when native `bind` is used. + +### \[shorthand-deprecated-v3\] JQMIGRATE: jQuery.fn.click() event shorthand is deprecated + +**Cause:** The `.on()` and `.trigger()` methods can set an event handler or generate an event for any event type, and should be used instead of the shortcut methods. This message also applies to the other event shorthands, including: blur, focus, focusin, focusout, resize, scroll, dblclick, mousedown, mouseup, mousemove, mouseover, mouseout, mouseenter, mouseleave, change, select, submit, keydown, keypress, keyup, and contextmenu. + +**Solution:** Instead of `.click(fn)` use `.on("click", fn)`. Instead of `.click()` use `.trigger("click")`. + +### \[hover\] JQMIGRATE: jQuery.fn.hover() is deprecated + +**Cause:** The `.hover()` method is a shorthand for the use of the `mouseover`/`mouseout` events. It is often a poor user interface choice because it does not allow for any small amounts of delay between when the mouse enters or exits an area and when the event fires. This can make it quite difficult to use with UI widgets such as drop-down menus. For more information on the problems of hovering, see the [hoverIntent plugin](http://cherne.net/brian/resources/jquery.hoverIntent.html). + +**Solution:** Review uses of `.hover()` to determine if they are appropriate, and consider use of plugins such as `hoverIntent` as an alternative. The direct replacement for `.hover(fn1, fn2)`, is `.on("mouseenter", fn1).on("mouseleave", fn2)`.