From 0b776c15f202e9355cce919d94ed27b12957bc2b Mon Sep 17 00:00:00 2001 From: Timmy Willison Date: Thu, 8 Feb 2024 08:21:43 -0500 Subject: [PATCH] Core: Fix IE 10 and IE 11 compatibility of internal Promise with sinon.useFakeTimers The Promise polyfill needs to save the unmodified reference to the `setImmediate` function in IE, for use in `Promise._immediateFn`. This broke in QUnit 2.14.0, with the introduction of internal Promise usage as part of https://github.com/qunitjs/qunit/issues/1535. Closes https://github.com/qunitjs/qunit/pull/1738. --- lib/promise-polyfill.js | 18 +++++++++------- test/main/promise.js | 46 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 57 insertions(+), 7 deletions(-) diff --git a/lib/promise-polyfill.js b/lib/promise-polyfill.js index 0513c4a0b..d144ac46d 100644 --- a/lib/promise-polyfill.js +++ b/lib/promise-polyfill.js @@ -36,6 +36,8 @@ Patches for use in QUnit: - 2021-01-10: Add 'globalThis' to globalNS implementation to support SpiderMonkey. +- 2024-02-07: Save unmodified setImmediate to avoid conflicts with Sinon fake timers. + */ (function () { 'use strict'; @@ -373,16 +375,18 @@ Promise.race = function(arr) { }; // Use polyfill for setImmediate for performance gains -Promise._immediateFn = +// @ts-ignore +if (typeof setImmediate === 'function') { // @ts-ignore - (typeof setImmediate === 'function' && - function(fn) { - // @ts-ignore - setImmediate(fn); - }) || - function(fn) { + var setImmediateFunc = setImmediate; + Promise._immediateFn = function(fn) { + setImmediateFunc(fn); + }; +} else { + Promise._immediateFn = function(fn) { setTimeoutFunc(fn, 0); }; +} Promise._unhandledRejectionFn = function _unhandledRejectionFn(err) { if (typeof console !== 'undefined' && console) { diff --git a/test/main/promise.js b/test/main/promise.js index 8745e7da9..a828bb331 100644 --- a/test/main/promise.js +++ b/test/main/promise.js @@ -8,6 +8,27 @@ var defer = typeof setTimeout !== 'undefined' Promise.resolve().then(fn); }; +// Get the global namespace the same way as the Promise polyfill. +var globalNS = (function () { + if (typeof globalThis !== 'undefined') { + // eslint-disable-next-line no-undef + return globalThis; + } + if (typeof self !== 'undefined') { + // eslint-disable-next-line no-undef + return self; + } + if (typeof window !== 'undefined') { + // eslint-disable-next-line no-undef + return window; + } + if (typeof global !== 'undefined') { + // eslint-disable-next-line no-undef + return global; + } + throw new Error('unable to locate global object'); +})(); + // NOTE: Adds 1 assertion function createMockPromise (assert, reject, value) { if (arguments.length < 3) { @@ -247,4 +268,29 @@ QUnit.module('Support for Promise', function () { return createMockPromise(assert, true, new Error('this is an error')); }); + + QUnit.module('compatible with fake timers', { + beforeEach: function (assert) { + this.setTimeout = globalNS.setTimeout; + globalNS.setTimeout = function () {}; + if (globalNS.setImmediate) { + this.setImmediate = globalNS.setImmediate; + globalNS.setImmediate = function () {}; + } + // Adds 1 assertion + return createMockPromise(assert); + }, + afterEach: function (assert) { + globalNS.setTimeout = this.setTimeout; + if (this.setImmediate) { + globalNS.setImmediate = this.setImmediate; + } + // Adds 1 assertion + return createMockPromise(assert); + } + }); + + QUnit.test('test', function (assert) { + assert.expect(2); + }); });