Skip to content

Commit cfb7ee1

Browse files
authored
Always enforce the usage of the original Promise (#5077)
* Always enforce the usage of the original Promise * Only whitelist Promise
1 parent eccd711 commit cfb7ee1

13 files changed

Lines changed: 166 additions & 7 deletions

File tree

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
/**
2+
* Copyright (c) 2014-present, Facebook, Inc. All rights reserved.
3+
*
4+
* This source code is licensed under the MIT license found in the
5+
* LICENSE file in the root directory of this source tree.
6+
*
7+
* @flow
8+
*/
9+
10+
'use strict';
11+
12+
import runJest from '../runJest';
13+
14+
test('overriding native promise does not freeze Jest', () => {
15+
const run = runJest('override-globals');
16+
expect(run.stderr).toMatch(/PASS __tests__(\/|\\)index.js/);
17+
});
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
/**
2+
* Copyright (c) 2014-present, Facebook, Inc. All rights reserved.
3+
*
4+
* This source code is licensed under the MIT license found in the
5+
* LICENSE file in the root directory of this source tree.
6+
*/
7+
8+
'use strict';
9+
10+
import _unusedRequireOverridingPromise from '..';
11+
12+
describe('parent', () => {
13+
beforeEach(() => {
14+
console.log('Promise is: ' + Promise.toString());
15+
});
16+
17+
describe('child', () => {
18+
it('works well', () => {
19+
expect(() => new Promise()).toThrow('Booo');
20+
});
21+
});
22+
});
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
/**
2+
* Copyright (c) 2014-present, Facebook, Inc. All rights reserved.
3+
*
4+
* This source code is licensed under the MIT license found in the
5+
* LICENSE file in the root directory of this source tree.
6+
*
7+
* @flow
8+
*/
9+
10+
global.Promise = function() {
11+
throw new Error('Booo');
12+
};
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
{
2+
"jest": {
3+
"testEnvironment": "node"
4+
}
5+
}

packages/jest-jasmine2/package.json

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,6 @@
1616
"jest-matcher-utils": "^21.2.1",
1717
"jest-message-util": "^21.2.1",
1818
"jest-snapshot": "^21.2.1",
19-
"p-cancelable": "^0.3.0",
2019
"source-map-support": "^0.5.0"
2120
},
2221
"devDependencies": {

packages/jest-jasmine2/src/jasmine/Env.js

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,9 +31,13 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
3131
/* eslint-disable sort-keys */
3232

3333
import queueRunner from '../queue_runner';
34-
3534
import treeProcessor from '../tree_processor';
3635

36+
// Try getting the real promise object from the context, if available. Someone
37+
// could have overridden it in a test. Async functions return it implicitly.
38+
// eslint-disable-next-line no-unused-vars
39+
const Promise = global[Symbol.for('jest-native-promise')] || global.Promise;
40+
3741
export default function(j$) {
3842
function Env(options) {
3943
options = options || {};
Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
'use strict';
2+
3+
// Try getting the real promise object from the context, if available. Someone
4+
// could have overridden it in a test.
5+
const Promise = global[Symbol.for('jest-native-promise')] || global.Promise;
6+
7+
class CancelError extends Error {
8+
constructor() {
9+
super('Promise was canceled');
10+
this.name = 'CancelError';
11+
}
12+
}
13+
14+
class PCancelable {
15+
static fn(fn) {
16+
return function() {
17+
const args = [].slice.apply(arguments);
18+
return new PCancelable((onCancel, resolve, reject) => {
19+
args.unshift(onCancel);
20+
fn.apply(null, args).then(resolve, reject);
21+
});
22+
};
23+
}
24+
25+
constructor(executor) {
26+
this._pending = true;
27+
this._canceled = false;
28+
29+
this._promise = new Promise((resolve, reject) => {
30+
this._reject = reject;
31+
32+
return executor(
33+
fn => {
34+
this._cancel = fn;
35+
},
36+
val => {
37+
this._pending = false;
38+
resolve(val);
39+
},
40+
err => {
41+
this._pending = false;
42+
reject(err);
43+
},
44+
);
45+
});
46+
}
47+
48+
then() {
49+
return this._promise.then.apply(this._promise, arguments);
50+
}
51+
52+
catch() {
53+
return this._promise.catch.apply(this._promise, arguments);
54+
}
55+
56+
cancel() {
57+
if (!this._pending || this._canceled) {
58+
return;
59+
}
60+
61+
if (typeof this._cancel === 'function') {
62+
try {
63+
this._cancel();
64+
} catch (err) {
65+
this._reject(err);
66+
}
67+
}
68+
69+
this._canceled = true;
70+
this._reject(new CancelError());
71+
}
72+
73+
get canceled() {
74+
return this._canceled;
75+
}
76+
}
77+
78+
Object.setPrototypeOf(PCancelable.prototype, Promise.prototype);
79+
80+
module.exports = PCancelable;
81+
module.exports.CancelError = CancelError;

packages/jest-jasmine2/src/p_timeout.js

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,10 @@
77
* @flow
88
*/
99

10+
// Try getting the real promise object from the context, if available. Someone
11+
// could have overridden it in a test.
12+
const Promise = global[Symbol.for('jest-native-promise')] || global.Promise;
13+
1014
// A specialized version of `p-timeout` that does not touch globals.
1115
// It does not throw on timeout.
1216
export default function pTimeout(

packages/jest-jasmine2/src/queue_runner.js

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,12 @@
77
* @flow
88
*/
99

10-
import PCancelable from 'p-cancelable';
10+
// Try getting the real promise object from the context, if available. Someone
11+
// could have overridden it in a test.
12+
const Promise: Class<Promise> =
13+
global[Symbol.for('jest-native-promise')] || global.Promise;
14+
15+
import PCancelable from './p_cancelable';
1116
import pTimeout from './p_timeout';
1217

1318
type Options = {
@@ -55,7 +60,9 @@ export default function queueRunner(options: Options) {
5560
if (!timeout) {
5661
return promise;
5762
}
63+
5864
const timeoutMs: number = timeout();
65+
5966
return pTimeout(
6067
promise,
6168
timeoutMs,

packages/jest-jasmine2/src/reporter.js

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,10 @@ import type {
1717
TestResult,
1818
} from 'types/TestResult';
1919

20+
// Try getting the real promise object from the context, if available. Someone
21+
// could have overridden it in a test.
22+
const Promise = global[Symbol.for('jest-native-promise')] || global.Promise;
23+
2024
import {formatResultsErrors} from 'jest-message-util';
2125

2226
type Suite = {

0 commit comments

Comments
 (0)