Adding capabilities class to manage rAF/rIC state
ADD
addon/capabilities.js
+const CAPABILITIES = {
+ requestAnimationFrame: requestAnimationFrame,
+ requestIdleCallback: requestIdleCallback,
+};
+
+/**
+ * @type {Capabilities}
+ */
+let _instance = null;
+
+class Capabilities {
+ constructor() {
+ if (_instance) {
+ throw new Error('Capabilities can only be constructed once');
+ }
+
+ this.reset();
+ this.DISABLED = true;
+ }
+
+ static get instance() {
+ if (!_instance) {
+ _instance = new Capabilities();
+ }
+
+ return _instance;
+ }
+
+ overrideCapability(capability, override = false) {
+ this[capability + 'Enabled'] = override
+ ? false
+ : typeof CAPABILITIES[capability] === 'function';
+ }
+
+ reset() {
+ this.overrideCapability('requestAnimationFrame');
+ this.overrideCapability('requestIdleCallback');
+ }
+}
+
+export default Capabilities;

MODIFY
addon/scheduler.js
addon/scheduler.js
@@ -2,15 +2,16 @@ import RSVP from 'rsvp';
import { run } from '@ember/runloop';
import { DEBUG } from '@glimmer/env';
import { registerWaiter } from '@ember/test';
+import Capabilities from './capabilities';
+const CAPABILITIES = Capabilities.instance;
const APP_SCHEDULER_HAS_SETUP = '__APP_SCHEDULER_HAS_SETUP__';
+
let _didTransition;
let _whenRoutePainted;
let _whenRoutePaintedScheduleFn;
let _whenRouteIdle;
let _whenRouteIdleScheduleFn;
-let _requestAnimationFrameEnabled;
-let _requestIdleCallbackEnabled;
let _activeScheduledTasks = 0;
export const USE_REQUEST_IDLE_CALLBACK = true;
@@ -90,30 +91,16 @@ export function routeSettled() {
return _whenRouteIdle;
}
-export function _useRequestAnimationFrame(
- rAFEnabled = typeof requestAnimationFrame === 'function'
-) {
- _requestAnimationFrameEnabled = rAFEnabled;
-}
-
-export function _useRequestIdleCallback(
- rICEnabled = typeof requestIdleCallback === 'function'
-) {
- _requestIdleCallbackEnabled = rICEnabled;
-}
-
export function _getScheduleFn(useRequestIdleCallback = false) {
- if (useRequestIdleCallback && _requestIdleCallbackEnabled) {
+ if (useRequestIdleCallback && CAPABILITIES.requestIdleCallbackEnabled) {
return requestIdleCallback;
- } else if (_requestAnimationFrameEnabled) {
+ } else if (CAPABILITIES.requestAnimationFrameEnabled) {
return requestAnimationFrame;
} else {
return SIMPLE_CALLBACK;
}
}
-_useRequestAnimationFrame();
-_useRequestIdleCallback();
_whenRoutePaintedScheduleFn = _getScheduleFn();
_whenRouteIdleScheduleFn = _getScheduleFn(USE_REQUEST_IDLE_CALLBACK);

ADD
tests/unit/capabilities-test.js
+import { module, test } from 'qunit';
+import Capabilities from 'ember-app-scheduler/capabilities';
+
+const CAPABILITIES = Capabilities.instance;
+
+module('Unit | Capabilities', function(hooks) {
+ hooks.beforeEach(function() {
+ debugger;
+ CAPABILITIES.reset();
+ });
+
+ test('capabilities is initialized correctly', function(assert) {
+ assert.expect(2);
+ debugger;
+ assert.ok(CAPABILITIES.requestAnimationFrameEnabled);
+ assert.ok(CAPABILITIES.requestIdleCallbackEnabled);
+ });
+
+ test('capabilities can be overridden correctly', function(assert) {
+ assert.expect(2);
+
+ CAPABILITIES.overrideCapability('requestAnimationFrame', true);
+ CAPABILITIES.overrideCapability('requestIdleCallback', true);
+
+ assert.equal(CAPABILITIES.requestAnimationFrameEnabled, false);
+ assert.equal(CAPABILITIES.requestIdleCallbackEnabled, false);
+ });
+});

MODIFY
tests/unit/scheduler-test.js
tests/unit/scheduler-test.js
@@ -7,20 +7,19 @@ import {
endTransition,
} from 'ember-app-scheduler';
import {
- _useRequestAnimationFrame,
- _useRequestIdleCallback,
_getScheduleFn,
USE_REQUEST_IDLE_CALLBACK,
SIMPLE_CALLBACK,
} from 'ember-app-scheduler/scheduler';
+import Capabilities from 'ember-app-scheduler/capabilities';
+const CAPABILITIES = Capabilities.instance;
const REQUEST_ANIMATION_FRAME = requestAnimationFrame;
module('Unit | Scheduler', function(hooks) {
hooks.afterEach(function() {
reset();
- _useRequestAnimationFrame();
- _useRequestIdleCallback();
+ CAPABILITIES.reset();
window.requestAnimationFrame = REQUEST_ANIMATION_FRAME;
});
@@ -43,7 +42,7 @@ module('Unit | Scheduler', function(hooks) {
test('whenRouteIdle resolves when transition ended when requestAnimationFrame not available', async function(assert) {
assert.expect(1);
- _useRequestAnimationFrame(false);
+ CAPABILITIES.overrideCapability('requestAnimationFrame', true);
window.requestAnimationFrame = () =>
assert.ok(false, 'requestAnimationFrame was used');
beginTransition();
@@ -59,6 +58,25 @@ module('Unit | Scheduler', function(hooks) {
await routeSettled();
});
+ test('whenRouteIdle resolves using requestAnimationFrame when transition ended when requestIdleCallback not available', async function(assert) {
+ assert.expect(1);
+ debugger;
+ CAPABILITIES.overrideCapability('requestIdleCallback', true);
+ window.requestAnimationFrame = () =>
+ assert.ok(true, 'requestAnimationFrame was used');
+ beginTransition();
+
+ let routeIdle = whenRouteIdle();
+
+ endTransition();
+
+ await routeIdle.then(() => {
+ assert.ok(true);
+ });
+
+ await routeSettled();
+ });
+
test('whenRouteIdle with transition interupted', async function(assert) {
assert.expect(3);
@@ -89,7 +107,7 @@ module('Unit | Scheduler', function(hooks) {
});
test('_getScheduleFn falls back to requestAnimationFrame if requestIdleCallback not available', function(assert) {
- _useRequestIdleCallback(false);
+ CAPABILITIES.overrideCapability('requestIdleCallback', true);
assert.equal(
_getScheduleFn(USE_REQUEST_IDLE_CALLBACK),
@@ -98,8 +116,8 @@ module('Unit | Scheduler', function(hooks) {
});
test('_getScheduleFn returns simple callback if requestAnimationFrame not available', function(assert) {
- _useRequestIdleCallback(false);
- _useRequestAnimationFrame(false);
+ CAPABILITIES.overrideCapability('requestAnimationFrame', true);
+ CAPABILITIES.overrideCapability('requestIdleCallback', true);
assert.equal(_getScheduleFn(), SIMPLE_CALLBACK);
});

Adding capabilities class to manage rAF/rIC state