mirror of
https://github.com/zebrajr/react.git
synced 2026-01-15 12:15:22 +00:00
Fixed scheduler setTimeout fallback (#14358)
* Fixed scheduler setTimeout fallback * Moved unit-test-specific setTimeout code into a new NPM package, jest-mock-scheduler.
This commit is contained in:
5
packages/jest-mock-scheduler/README.md
Normal file
5
packages/jest-mock-scheduler/README.md
Normal file
@@ -0,0 +1,5 @@
|
||||
# `jest-mock-scheduler`
|
||||
|
||||
Jest matchers and utilities for testing the `scheduler` package.
|
||||
|
||||
This package is experimental. APIs may change between releases.
|
||||
8
packages/jest-mock-scheduler/index.js
Normal file
8
packages/jest-mock-scheduler/index.js
Normal file
@@ -0,0 +1,8 @@
|
||||
/**
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
export * from './src/JestMockScheduler';
|
||||
7
packages/jest-mock-scheduler/npm/index.js
Normal file
7
packages/jest-mock-scheduler/npm/index.js
Normal file
@@ -0,0 +1,7 @@
|
||||
'use strict';
|
||||
|
||||
if (process.env.NODE_ENV === 'production') {
|
||||
module.exports = require('./cjs/jest-mock-scheduler.production.min.js');
|
||||
} else {
|
||||
module.exports = require('./cjs/jest-mock-scheduler.development.js');
|
||||
}
|
||||
28
packages/jest-mock-scheduler/package.json
Normal file
28
packages/jest-mock-scheduler/package.json
Normal file
@@ -0,0 +1,28 @@
|
||||
{
|
||||
"name": "jest-mock-scheduler",
|
||||
"private": true,
|
||||
"version": "0.1.0",
|
||||
"description": "Jest matchers and utilities for testing the scheduler package.",
|
||||
"main": "index.js",
|
||||
"repository": "facebook/react",
|
||||
"keywords": [
|
||||
"jest",
|
||||
"scheduler"
|
||||
],
|
||||
"license": "MIT",
|
||||
"bugs": {
|
||||
"url": "https://github.com/facebook/react/issues"
|
||||
},
|
||||
"homepage": "https://reactjs.org/",
|
||||
"peerDependencies": {
|
||||
"jest": "^23.0.1",
|
||||
"scheduler": "^0.11.0"
|
||||
},
|
||||
"files": [
|
||||
"LICENSE",
|
||||
"README.md",
|
||||
"build-info.json",
|
||||
"index.js",
|
||||
"cjs/"
|
||||
]
|
||||
}
|
||||
61
packages/jest-mock-scheduler/src/JestMockScheduler.js
Normal file
61
packages/jest-mock-scheduler/src/JestMockScheduler.js
Normal file
@@ -0,0 +1,61 @@
|
||||
/**
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
// Max 31 bit integer. The max integer size in V8 for 32-bit systems.
|
||||
// Math.pow(2, 30) - 1
|
||||
// 0b111111111111111111111111111111
|
||||
const maxSigned31BitInt = 1073741823;
|
||||
|
||||
export function mockRestore() {
|
||||
delete global._schedMock;
|
||||
}
|
||||
|
||||
let callback = null;
|
||||
let currentTime = -1;
|
||||
|
||||
function flushCallback(didTimeout, ms) {
|
||||
if (callback !== null) {
|
||||
let cb = callback;
|
||||
callback = null;
|
||||
try {
|
||||
currentTime = ms;
|
||||
cb(didTimeout);
|
||||
} finally {
|
||||
currentTime = -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function requestHostCallback(cb, ms) {
|
||||
if (currentTime !== -1) {
|
||||
// Protect against re-entrancy.
|
||||
setTimeout(requestHostCallback, 0, cb, ms);
|
||||
} else {
|
||||
callback = cb;
|
||||
setTimeout(flushCallback, ms, true, ms);
|
||||
setTimeout(flushCallback, maxSigned31BitInt, false, maxSigned31BitInt);
|
||||
}
|
||||
}
|
||||
|
||||
function cancelHostCallback() {
|
||||
callback = null;
|
||||
}
|
||||
|
||||
function shouldYieldToHost() {
|
||||
return false;
|
||||
}
|
||||
|
||||
function getCurrentTime() {
|
||||
return currentTime === -1 ? 0 : currentTime;
|
||||
}
|
||||
|
||||
global._schedMock = [
|
||||
requestHostCallback,
|
||||
cancelHostCallback,
|
||||
shouldYieldToHost,
|
||||
getCurrentTime,
|
||||
];
|
||||
@@ -447,12 +447,20 @@ var requestHostCallback;
|
||||
var cancelHostCallback;
|
||||
var shouldYieldToHost;
|
||||
|
||||
if (typeof window !== 'undefined' && window._schedMock) {
|
||||
var globalValue = null;
|
||||
if (typeof window !== 'undefined') {
|
||||
globalValue = window;
|
||||
} else if (typeof global !== 'undefined') {
|
||||
globalValue = global;
|
||||
}
|
||||
|
||||
if (globalValue && globalValue._schedMock) {
|
||||
// Dynamic injection, only for testing purposes.
|
||||
var impl = window._schedMock;
|
||||
requestHostCallback = impl[0];
|
||||
cancelHostCallback = impl[1];
|
||||
shouldYieldToHost = impl[2];
|
||||
var globalImpl = globalValue._schedMock;
|
||||
requestHostCallback = globalImpl[0];
|
||||
cancelHostCallback = globalImpl[1];
|
||||
shouldYieldToHost = globalImpl[2];
|
||||
getCurrentTime = globalImpl[3];
|
||||
} else if (
|
||||
// If Scheduler runs in a non-DOM environment, it falls back to a naive
|
||||
// implementation using setTimeout.
|
||||
@@ -460,28 +468,23 @@ if (typeof window !== 'undefined' && window._schedMock) {
|
||||
// Check if MessageChannel is supported, too.
|
||||
typeof MessageChannel !== 'function'
|
||||
) {
|
||||
// If this accidentally gets imported in a non-browser environment, e.g. JavaScriptCore,
|
||||
// fallback to a naive implementation.
|
||||
var _callback = null;
|
||||
var _currentTime = -1;
|
||||
var _flushCallback = function(didTimeout, ms) {
|
||||
var _flushCallback = function(didTimeout) {
|
||||
if (_callback !== null) {
|
||||
var cb = _callback;
|
||||
_callback = null;
|
||||
try {
|
||||
_currentTime = ms;
|
||||
cb(didTimeout);
|
||||
} finally {
|
||||
_currentTime = -1;
|
||||
}
|
||||
cb(didTimeout);
|
||||
}
|
||||
};
|
||||
requestHostCallback = function(cb, ms) {
|
||||
if (_currentTime !== -1) {
|
||||
if (_callback !== null) {
|
||||
// Protect against re-entrancy.
|
||||
setTimeout(requestHostCallback, 0, cb, ms);
|
||||
setTimeout(requestHostCallback, 0, cb);
|
||||
} else {
|
||||
_callback = cb;
|
||||
setTimeout(_flushCallback, ms, true, ms);
|
||||
setTimeout(_flushCallback, maxSigned31BitInt, false, maxSigned31BitInt);
|
||||
setTimeout(_flushCallback, 0, false);
|
||||
}
|
||||
};
|
||||
cancelHostCallback = function() {
|
||||
@@ -490,9 +493,6 @@ if (typeof window !== 'undefined' && window._schedMock) {
|
||||
shouldYieldToHost = function() {
|
||||
return false;
|
||||
};
|
||||
getCurrentTime = function() {
|
||||
return _currentTime === -1 ? 0 : _currentTime;
|
||||
};
|
||||
} else {
|
||||
if (typeof console !== 'undefined') {
|
||||
// TODO: Remove fb.me link
|
||||
|
||||
@@ -30,6 +30,9 @@ describe('Scheduler', () => {
|
||||
jest.useFakeTimers();
|
||||
jest.resetModules();
|
||||
|
||||
const JestMockScheduler = require('jest-mock-scheduler');
|
||||
JestMockScheduler.mockRestore();
|
||||
|
||||
let _flushWork = null;
|
||||
let isFlushing = false;
|
||||
let timeoutID = -1;
|
||||
@@ -123,16 +126,21 @@ describe('Scheduler', () => {
|
||||
function shouldYieldToHost() {
|
||||
return endOfFrame <= currentTime;
|
||||
}
|
||||
function getCurrentTime() {
|
||||
return currentTime;
|
||||
}
|
||||
|
||||
// Override host implementation
|
||||
delete global.performance;
|
||||
global.Date.now = () => {
|
||||
return currentTime;
|
||||
};
|
||||
|
||||
window._schedMock = [
|
||||
requestHostCallback,
|
||||
cancelHostCallback,
|
||||
shouldYieldToHost,
|
||||
getCurrentTime,
|
||||
];
|
||||
|
||||
const Schedule = require('scheduler');
|
||||
|
||||
@@ -88,6 +88,10 @@ describe('SchedulerDOM', () => {
|
||||
return currentTime;
|
||||
};
|
||||
jest.resetModules();
|
||||
|
||||
const JestMockScheduler = require('jest-mock-scheduler');
|
||||
JestMockScheduler.mockRestore();
|
||||
|
||||
Scheduler = require('scheduler');
|
||||
});
|
||||
|
||||
|
||||
@@ -22,6 +22,10 @@ describe('ReactDOMFrameScheduling', () => {
|
||||
};
|
||||
};
|
||||
jest.resetModules();
|
||||
|
||||
const JestMockScheduler = require('jest-mock-scheduler');
|
||||
JestMockScheduler.mockRestore();
|
||||
|
||||
spyOnDevAndProd(console, 'error');
|
||||
require('react-dom');
|
||||
expect(console.error.calls.count()).toEqual(1);
|
||||
|
||||
@@ -56,6 +56,8 @@ if (process.env.REACT_CLASS_EQUIVALENCE_TEST) {
|
||||
toMatchRenderedOutput: JestReact.unstable_toMatchRenderedOutput,
|
||||
});
|
||||
|
||||
require('jest-mock-scheduler');
|
||||
|
||||
// We have a Babel transform that inserts guards against infinite loops.
|
||||
// If a loop runs for too many iterations, we throw an error and set this
|
||||
// global variable. The global lets us detect an infinite loop even if
|
||||
|
||||
@@ -462,6 +462,15 @@ const bundles = [
|
||||
externals: ['jest-diff'],
|
||||
},
|
||||
|
||||
/******* Jest Scheduler (experimental) *******/
|
||||
{
|
||||
bundleTypes: [NODE_DEV, NODE_PROD, FB_WWW_DEV, FB_WWW_PROD],
|
||||
moduleType: ISOMORPHIC,
|
||||
entry: 'jest-mock-scheduler',
|
||||
global: 'JestMockScheduler',
|
||||
externals: ['jest-diff'],
|
||||
},
|
||||
|
||||
/******* ESLint Plugin for Hooks (proposal) *******/
|
||||
{
|
||||
// TODO: it's awkward to create a bundle for this
|
||||
|
||||
Reference in New Issue
Block a user