From 44de3d325c1ac0c4a841deff0ec03265a0b670f7 Mon Sep 17 00:00:00 2001 From: Michał Gołębiowski-Owczarek Date: Fri, 21 Mar 2025 00:03:17 +0100 Subject: Spinner: Prevent double mousewheel & wheel event handling As of gh-2338, if one has loaded the jQuery MouseWheel plugin, the `mousewheel` handler would fire the `wheel` one, but the `wheel` one would also run in response to the native `wheel` event, resulting in double the distance handled by the spinner. To prevent the issue, only fire the `wheel` handler from inside the `mousewheel` on if the event was triggered by jQuery - jQuery will not care that the underlying event is `wheel` and will only fire handlers for `mousewheel`. Also, add an iframe test using jQuery MouseWheel to not affect all the other tests. Plus, migrate from `QUnit.reset` to `QUnit.done` (see qunitjs/qunit#354). Closes gh-2342 Ref gh-2338 --- tests/lib/helper.js | 59 ++++++++++++++++++++++++++ tests/lib/qunit.js | 23 ++++++---- tests/lib/testIframe.js | 7 ++++ tests/unit/spinner/core.js | 14 +++++++ tests/unit/spinner/mousewheel-wheel.html | 72 ++++++++++++++++++++++++++++++++ 5 files changed, 167 insertions(+), 8 deletions(-) create mode 100644 tests/lib/testIframe.js create mode 100644 tests/unit/spinner/mousewheel-wheel.html (limited to 'tests') diff --git a/tests/lib/helper.js b/tests/lib/helper.js index 2315c5e19..2be4c48de 100644 --- a/tests/lib/helper.js +++ b/tests/lib/helper.js @@ -51,6 +51,65 @@ exports.moduleAfterEach = function( assert ) { } }; +exports.testIframe = function( title, fileName, func, wrapper, iframeStyles ) { + if ( !wrapper ) { + wrapper = QUnit.test; + } + wrapper.call( QUnit, title, function( assert ) { + var done = assert.async(), + $iframe = jQuery( "" ) + .css( { + position: "absolute", + top: "0", + left: "-600px", + width: "500px", + zIndex: 1, + background: "white" + } ) + .attr( { id: "qunit-fixture-iframe", src: fileName } ); + + // Add other iframe styles + if ( iframeStyles ) { + $iframe.css( iframeStyles ); + } + + // Test iframes are expected to invoke this via startIframeTest + // (cf. iframeTest.js) + window.iframeCallback = function() { + var args = Array.prototype.slice.call( arguments ); + + args.unshift( assert ); + + setTimeout( function() { + var result; + + this.iframeCallback = undefined; + + result = func.apply( this, args ); + + function finish() { + func = function() {}; + $iframe.remove(); + done(); + } + + // Wait for promises returned by `func`. + if ( result && result.then ) { + result.then( finish ); + } else { + finish(); + } + } ); + }; + + // Attach iframe to the body for visibility-dependent code. + // It will be removed by either the above code, or the testDone + // callback in qunit.js. + $iframe.prependTo( document.body ); + } ); +}; +window.iframeCallback = undefined; + return exports; } ); diff --git a/tests/lib/qunit.js b/tests/lib/qunit.js index 6441019bd..c4c96ef58 100644 --- a/tests/lib/qunit.js +++ b/tests/lib/qunit.js @@ -7,6 +7,8 @@ define( [ ], function( QUnit, $ ) { "use strict"; +var ajaxSettings = $.ajaxSettings; + QUnit.config.autostart = false; QUnit.config.requireExpects = true; @@ -34,16 +36,21 @@ QUnit.config.urlConfig.push( { label: "Enable jquery-migrate" } ); -QUnit.reset = ( function( reset ) { - return function() { +QUnit.testDone( function() { + + // Ensure jQuery events and data on the fixture are properly removed + $( "#qunit-fixture" ).empty(); - // Ensure jQuery events and data on the fixture are properly removed - $( "#qunit-fixture" ).empty(); + // Remove the iframe fixture + $( "#qunit-fixture-iframe" ).remove(); - // Let QUnit reset the fixture - reset.apply( this, arguments ); - }; -} )( QUnit.reset ); + // Reset internal $ state + if ( ajaxSettings ) { + $.ajaxSettings = $.extend( true, {}, ajaxSettings ); + } else { + delete $.ajaxSettings; + } +} ); return QUnit; diff --git a/tests/lib/testIframe.js b/tests/lib/testIframe.js new file mode 100644 index 000000000..4db56833c --- /dev/null +++ b/tests/lib/testIframe.js @@ -0,0 +1,7 @@ +window.startIframeTest = function() { + var args = Array.prototype.slice.call( arguments ); + + // Note: jQuery may be undefined if page did not load it + args.unshift( window.jQuery, window, document ); + window.parent.iframeCallback.apply( null, args ); +}; diff --git a/tests/unit/spinner/core.js b/tests/unit/spinner/core.js index befe439f6..42bcc7bb5 100644 --- a/tests/unit/spinner/core.js +++ b/tests/unit/spinner/core.js @@ -239,6 +239,20 @@ QUnit.test( "mousewheel on input (DEPRECATED)", function( assert ) { } } ); +helper.testIframe( + "wheel & mousewheel conflicts", + "mousewheel-wheel.html", + function( assert, jQuery, window, document, values ) { + assert.expect( 5 ); + + assert.equal( values[ 0 ], 0, "wheel event without delta does not change value" ); + assert.equal( values[ 1 ], 2, "delta -1" ); + assert.equal( values[ 2 ], 0, "delta 0.2" ); + assert.equal( values[ 3 ], -2, "delta 15" ); + assert.equal( values[ 4 ], -2, "wheel when not focused" ); + } +); + QUnit.test( "reading HTML5 attributes", function( assert ) { assert.expect( 6 ); var markup = "", diff --git a/tests/unit/spinner/mousewheel-wheel.html b/tests/unit/spinner/mousewheel-wheel.html new file mode 100644 index 000000000..e512a36cc --- /dev/null +++ b/tests/unit/spinner/mousewheel-wheel.html @@ -0,0 +1,72 @@ + + + + + jQuery UI Spinner Test Suite + + + + + + + + + + + + + -- cgit v1.2.3