From 5596d278af0b37e7059eb3aed1cc47659ec1fe20 Mon Sep 17 00:00:00 2001 From: Robert Jackson Date: Thu, 14 Feb 2019 14:12:58 -0500 Subject: [PATCH] [BUGFIX beta] Update Backburner.js to 2.5.0. The primary change in Backburner.js is to use the Promise microtask queue primarily and falling back to MutationObserver when Promise is not present. The test changes here are to migrate from `window.onerror` to `window.onunhandledrejection` hooks (due to moving the microtask delivery from `MutationObserver` to native `Promise`). The cross platform support for `onunhandledrejection` is a tad spotty (Chrome, Edge, and _soon_ Firefox), so I had to guard in other browsers. --- package.json | 2 +- packages/ember-testing/tests/adapters_test.js | 29 +++-- packages/ember/tests/error_handler_test.js | 116 +++++++++++------- yarn.lock | 8 +- 4 files changed, 99 insertions(+), 56 deletions(-) diff --git a/package.json b/package.json index c4fe765e19b..6d1141f8442 100644 --- a/package.json +++ b/package.json @@ -108,7 +108,7 @@ "babel-plugin-filter-imports": "^2.0.4", "babel-plugin-module-resolver": "^3.2.0", "babel-template": "^6.26.0", - "backburner.js": "^2.4.2", + "backburner.js": "^2.5.0", "broccoli-babel-transpiler": "^7.1.2", "broccoli-concat": "^3.7.3", "broccoli-debug": "^0.6.4", diff --git a/packages/ember-testing/tests/adapters_test.js b/packages/ember-testing/tests/adapters_test.js index 943ed901c26..3dd4bbd049c 100644 --- a/packages/ember-testing/tests/adapters_test.js +++ b/packages/ember-testing/tests/adapters_test.js @@ -8,10 +8,12 @@ import { moduleFor, AbstractTestCase } from 'internal-test-helpers'; import { RSVP } from '@ember/-internals/runtime'; import { getDebugFunction, setDebugFunction } from '@ember/debug'; +const HAS_UNHANDLED_REJECTION_HANDLER = 'onunhandledrejection' in window; + const originalDebug = getDebugFunction('debug'); const noop = function() {}; -var App, originalAdapter, originalQUnit, originalWindowOnerror; +var App, originalAdapter, originalQUnit, originalWindowOnerror, originalQUnitUnhandledRejection; var originalConsoleError = console.error; // eslint-disable-line no-console @@ -32,15 +34,14 @@ class AdapterSetupAndTearDown extends AbstractTestCase { setDebugFunction('debug', noop); super(); originalAdapter = Test.adapter; - originalQUnit = window.QUnit; + originalQUnit = QUnit; originalWindowOnerror = window.onerror; + originalQUnitUnhandledRejection = QUnit.onUnhandledRejection; } afterEach() { - console.error = originalConsoleError; // eslint-disable-line no-console - } + super.afterEach(); - teardown() { setDebugFunction('debug', originalDebug); if (App) { run(App, App.destroy); @@ -52,6 +53,8 @@ class AdapterSetupAndTearDown extends AbstractTestCase { window.QUnit = originalQUnit; window.onerror = originalWindowOnerror; setOnerror(undefined); + console.error = originalConsoleError; // eslint-disable-line no-console + QUnit.onUnhandledRejection = originalQUnitUnhandledRejection; } } @@ -240,6 +243,11 @@ moduleFor( function testAdapter(message, generatePromise, timeout = 10) { return class PromiseFailureTests extends AdapterSetupAndTearDown { [`@test ${message} when TestAdapter without \`exception\` method is present - rsvp`](assert) { + if (!HAS_UNHANDLED_REJECTION_HANDLER) { + assert.expect(0); + return; + } + assert.expect(1); let thrown = new Error('the error'); @@ -247,13 +255,16 @@ function testAdapter(message, generatePromise, timeout = 10) { exception: undefined, }); - window.onerror = function(message) { + // prevent QUnit handler from failing test + QUnit.onUnhandledRejection = () => {}; + + window.onunhandledrejection = function(rejection) { assert.pushResult({ - result: /the error/.test(message), - actual: message, + result: /the error/.test(rejection.reason), + actual: rejection.reason, expected: 'to include `the error`', message: - 'error should bubble out to window.onerror, and therefore fail tests (due to QUnit implementing window.onerror)', + 'error should bubble out to window.onunhandledrejection, and therefore fail tests (due to QUnit implementing window.onunhandledrejection)', }); // prevent "bubbling" and therefore failing the test diff --git a/packages/ember/tests/error_handler_test.js b/packages/ember/tests/error_handler_test.js index ce419ddca3b..d2eb79b6a3e 100644 --- a/packages/ember/tests/error_handler_test.js +++ b/packages/ember/tests/error_handler_test.js @@ -4,6 +4,8 @@ import { getOnerror, setOnerror } from '@ember/-internals/error-handling'; import RSVP from 'rsvp'; import { moduleFor, AbstractTestCase } from 'internal-test-helpers'; +const HAS_UNHANDLED_REJECTION_HANDLER = 'onunhandledrejection' in window; +let QUNIT_ON_UNHANDLED_REJECTION = QUnit.onUnhandledRejection; let WINDOW_ONERROR; function runThatThrowsSync(message = 'Error for testing error handling') { @@ -26,6 +28,7 @@ moduleFor( window.onerror = WINDOW_ONERROR; setOnerror(undefined); + QUnit.onUnhandledRejection = QUNIT_ON_UNHANDLED_REJECTION; } ['@test by default there is no onerror - sync run'](assert) { @@ -238,6 +241,11 @@ moduleFor( [`@test errors in promise constructor when Ember.onerror which does rethrow is present - rsvp`]( assert ) { + if (!HAS_UNHANDLED_REJECTION_HANDLER) { + assert.expect(0); + return; + } + assert.expect(2); let thrown = new Error('the error'); @@ -250,17 +258,17 @@ moduleFor( throw error; }); - window.onerror = function(message) { + // prevent QUnit handler from failing test + QUnit.onUnhandledRejection = () => {}; + + window.onunhandledrejection = function(event) { assert.pushResult({ - result: /the error/.test(message), - actual: message, + result: /the error/.test(event.reason), + actual: event.reason, expected: 'to include `the error`', message: - 'error should bubble out to window.onerror, and therefore fail tests (due to QUnit implementing window.onerror)', + 'error should bubble out to window.onunhandledrejection, and therefore fail tests (due to QUnit implementing window.onunhandledrejection)', }); - - // prevent "bubbling" and therefore failing the test - return true; }; new RSVP.Promise(() => { @@ -299,6 +307,11 @@ moduleFor( [`@test errors in promise constructor when Ember.onerror which does rethrow is present (Ember.testing = false) - rsvp`]( assert ) { + if (!HAS_UNHANDLED_REJECTION_HANDLER) { + assert.expect(0); + return; + } + assert.expect(2); setTesting(false); @@ -312,17 +325,17 @@ moduleFor( throw error; }); - window.onerror = function(message) { + // prevent QUnit handler from failing test + QUnit.onUnhandledRejection = () => {}; + + window.onunhandledrejection = function(event) { assert.pushResult({ - result: /the error/.test(message), - actual: message, + result: /the error/.test(event.reason), + actual: event.reason, expected: 'to include `the error`', message: - 'error should bubble out to window.onerror, and therefore fail tests (due to QUnit implementing window.onerror)', + 'error should bubble out to window.onunhandledrejection, and therefore fail tests (due to QUnit implementing window.onunhandledrejection)', }); - - // prevent "bubbling" and therefore failing the test - return true; }; new RSVP.Promise(() => { @@ -360,6 +373,11 @@ moduleFor( [`@test errors in promise .then callback when Ember.onerror which does rethrow is present - rsvp`]( assert ) { + if (!HAS_UNHANDLED_REJECTION_HANDLER) { + assert.expect(0); + return; + } + assert.expect(2); let thrown = new Error('the error'); @@ -372,17 +390,17 @@ moduleFor( throw error; }); - window.onerror = function(message) { + // prevent QUnit handler from failing test + QUnit.onUnhandledRejection = () => {}; + + window.onunhandledrejection = function(event) { assert.pushResult({ - result: /the error/.test(message), - actual: message, + result: /the error/.test(event.reason), + actual: event.reason, expected: 'to include `the error`', message: - 'error should bubble out to window.onerror, and therefore fail tests (due to QUnit implementing window.onerror)', + 'error should bubble out to window.onunhandledrejection, and therefore fail tests (due to QUnit implementing window.onunhandledrejection)', }); - - // prevent "bubbling" and therefore failing the test - return true; }; RSVP.resolve().then(() => { @@ -421,6 +439,11 @@ moduleFor( [`@test errors in promise .then callback when Ember.onerror which does rethrow is present (Ember.testing = false) - rsvp`]( assert ) { + if (!HAS_UNHANDLED_REJECTION_HANDLER) { + assert.expect(0); + return; + } + assert.expect(2); setTesting(false); @@ -434,17 +457,17 @@ moduleFor( throw error; }); - window.onerror = function(message) { + // prevent QUnit handler from failing test + QUnit.onUnhandledRejection = () => {}; + + window.onunhandledrejection = function(event) { assert.pushResult({ - result: /the error/.test(message), - actual: message, + result: /the error/.test(event.reason), + actual: event.reason, expected: 'to include `the error`', message: - 'error should bubble out to window.onerror, and therefore fail tests (due to QUnit implementing window.onerror)', + 'error should bubble out to window.onunhandledrejection, and therefore fail tests (due to QUnit implementing window.onunhandledrejection)', }); - - // prevent "bubbling" and therefore failing the test - return true; }; RSVP.resolve().then(() => { @@ -482,6 +505,11 @@ moduleFor( [`@test errors in async promise .then callback when Ember.onerror which does rethrow is present - rsvp`]( assert ) { + if (!HAS_UNHANDLED_REJECTION_HANDLER) { + assert.expect(0); + return; + } + assert.expect(2); let thrown = new Error('the error'); @@ -494,17 +522,17 @@ moduleFor( throw error; }); - window.onerror = function(message) { + // prevent QUnit handler from failing test + QUnit.onUnhandledRejection = () => {}; + + window.onunhandledrejection = function(event) { assert.pushResult({ - result: /the error/.test(message), - actual: message, + result: /the error/.test(event.reason), + actual: event.reason, expected: 'to include `the error`', message: - 'error should bubble out to window.onerror, and therefore fail tests (due to QUnit implementing window.onerror)', + 'error should bubble out to window.onunhandledrejection, and therefore fail tests (due to QUnit implementing window.onunhandledrejection)', }); - - // prevent "bubbling" and therefore failing the test - return true; }; new RSVP.Promise(resolve => setTimeout(resolve, 10)).then(() => { @@ -543,6 +571,10 @@ moduleFor( [`@test errors in async promise .then callback when Ember.onerror which does rethrow is present (Ember.testing = false) - rsvp`]( assert ) { + if (!HAS_UNHANDLED_REJECTION_HANDLER) { + assert.expect(0); + return; + } assert.expect(2); setTesting(false); @@ -556,17 +588,17 @@ moduleFor( throw error; }); - window.onerror = function(message) { + // prevent QUnit handler from failing test + QUnit.onUnhandledRejection = () => {}; + + window.onunhandledrejection = function(event) { assert.pushResult({ - result: /the error/.test(message), - actual: message, + result: /the error/.test(event.reason), + actual: event.reason, expected: 'to include `the error`', message: - 'error should bubble out to window.onerror, and therefore fail tests (due to QUnit implementing window.onerror)', + 'error should bubble out to window.onunhandledrejection, and therefore fail tests (due to QUnit implementing window.onunhandledrejection)', }); - - // prevent "bubbling" and therefore failing the test - return true; }; new RSVP.Promise(resolve => setTimeout(resolve, 10)).then(() => { diff --git a/yarn.lock b/yarn.lock index deb582db2e5..5a74b47db9f 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1339,10 +1339,10 @@ backbone@^1.1.2: dependencies: underscore ">=1.8.3" -backburner.js@^2.4.2: - version "2.4.2" - resolved "https://registry.yarnpkg.com/backburner.js/-/backburner.js-2.4.2.tgz#4cc2d3e78592ddd46ae9a48cfc168ca883b56e6b" - integrity sha512-YFgfwWvelbJ9JxsVF+sph7nLB2qQfwDyUXCr+ZPyeU6HOXGAT0Rt+ro+mU1zRMnUfYG57nwaTiz1yqvqNO6jQQ== +backburner.js@^2.5.0: + version "2.5.0" + resolved "https://registry.yarnpkg.com/backburner.js/-/backburner.js-2.5.0.tgz#d03ab8f7b77e708ac4bc7bea2258aa57177b560d" + integrity sha512-YkLvnh0jEA67NNJ+NqJrX4Yb/isanR7XHlb+Y+cMXkGtXzlgx2tHEnZPIWi2kbW9EwtCq3HTRcAF+mbzIhfoqg== backo2@1.0.2: version "1.0.2"