Skip to content

Commit 66987ed

Browse files
JLHwungthymikee
authored andcommitted
feat: Import jest-zone-patch (#214)
This PR is addressed to thymikee/jest-zone-patch#9 (comment). For the sake of complete git history, I merge the commits into this repository. There is another LICENSE issue: The `jest-zone-patch` is licensed under BSD-3-Clause while this repository under MIT. I keep the original BSD LICENSE on this PR and left it as-is.
1 parent 52a4878 commit 66987ed

File tree

5 files changed

+142
-2
lines changed

5 files changed

+142
-2
lines changed

package.json

-1
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@
88
"license": "MIT",
99
"dependencies": {
1010
"@types/jest": "^23.3.10",
11-
"jest-zone-patch": ">=0.0.9 <1.0.0",
1211
"ts-jest": "~23.10.0"
1312
},
1413
"devDependencies": {

setupJest.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ require('zone.js/dist/proxy.js');
77
require('zone.js/dist/sync-test');
88
require('zone.js/dist/async-test');
99
require('zone.js/dist/fake-async-test');
10-
require('jest-zone-patch');
10+
require('./zone-patch');
1111

1212
const getTestBed = require('@angular/core/testing').getTestBed;
1313
const BrowserDynamicTestingModule = require('@angular/platform-browser-dynamic/testing').BrowserDynamicTestingModule;

zone-patch/LICENSE

+29
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
BSD 3-Clause License
2+
3+
Copyright (c) 2017, Michał Pierzchała
4+
All rights reserved.
5+
6+
Redistribution and use in source and binary forms, with or without
7+
modification, are permitted provided that the following conditions are met:
8+
9+
* Redistributions of source code must retain the above copyright notice, this
10+
list of conditions and the following disclaimer.
11+
12+
* Redistributions in binary form must reproduce the above copyright notice,
13+
this list of conditions and the following disclaimer in the documentation
14+
and/or other materials provided with the distribution.
15+
16+
* Neither the name of the copyright holder nor the names of its
17+
contributors may be used to endorse or promote products derived from
18+
this software without specific prior written permission.
19+
20+
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
21+
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22+
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
23+
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
24+
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25+
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
26+
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
27+
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
28+
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
29+
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

zone-patch/README.md

+6
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
# zone-patch
2+
Enables Jest functions to be run within Zone.js context, specifically for [Angular](https://angular.io) apps.
3+
4+
It's crucial to run this patch here, because at this point patched functions like `test` or `describe` are available in global scope.
5+
6+
`zone-patch` has been included in `setupJest.js` by default.

zone-patch/index.js

+106
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,106 @@
1+
/**
2+
* Patch Jest's describe/test/beforeEach/afterEach functions so test code
3+
* always runs in a testZone (ProxyZone).
4+
*/
5+
6+
if (Zone === undefined) {
7+
throw new Error('Missing: Zone (zone.js)');
8+
}
9+
if (jest === undefined) {
10+
throw new Error(
11+
'Missing: jest.\n' +
12+
'This patch must be included in a script called with ' +
13+
'`setupTestFrameworkScriptFile` in Jest config.'
14+
);
15+
}
16+
if (jest['__zone_patch__'] === true) {
17+
throw new Error("'jest' has already been patched with 'Zone'.");
18+
}
19+
20+
jest['__zone_patch__'] = true;
21+
const SyncTestZoneSpec = Zone['SyncTestZoneSpec'];
22+
const ProxyZoneSpec = Zone['ProxyZoneSpec'];
23+
24+
if (SyncTestZoneSpec === undefined) {
25+
throw new Error('Missing: SyncTestZoneSpec (zone.js/dist/sync-test)');
26+
}
27+
if (ProxyZoneSpec === undefined) {
28+
throw new Error('Missing: ProxyZoneSpec (zone.js/dist/proxy.js)');
29+
}
30+
31+
const env = global;
32+
const ambientZone = Zone.current;
33+
34+
// Create a synchronous-only zone in which to run `describe` blocks in order to
35+
// raise an error if any asynchronous operations are attempted
36+
// inside of a `describe` but outside of a `beforeEach` or `it`.
37+
const syncZone = ambientZone.fork(new SyncTestZoneSpec('jest.describe'));
38+
function wrapDescribeInZone(describeBody) {
39+
return function () { return syncZone.run(describeBody, null, arguments); }
40+
}
41+
42+
// Create a proxy zone in which to run `test` blocks so that the tests function
43+
// can retroactively install different zones.
44+
const testProxyZone = ambientZone.fork(new ProxyZoneSpec());
45+
function wrapTestInZone(testBody) {
46+
if (testBody === undefined) {
47+
return;
48+
}
49+
return testBody.length === 0
50+
? () => testProxyZone.run(testBody, null)
51+
: done => testProxyZone.run(testBody, null, [done]);
52+
}
53+
54+
const bindDescribe = (originalJestFn) => function () {
55+
const eachArguments = arguments;
56+
return function (description, specDefinitions, timeout) {
57+
arguments[1] = wrapDescribeInZone(specDefinitions)
58+
return originalJestFn.apply(this, eachArguments).apply(
59+
this,
60+
arguments
61+
)
62+
}
63+
};
64+
65+
['xdescribe', 'fdescribe', 'describe'].forEach(methodName => {
66+
const originaljestFn = env[methodName];
67+
env[methodName] = function(description, specDefinitions, timeout) {
68+
arguments[1] = wrapDescribeInZone(specDefinitions)
69+
return originaljestFn.apply(
70+
this,
71+
arguments
72+
);
73+
};
74+
env[methodName].each = bindDescribe(originaljestFn.each);
75+
if (methodName === 'describe') {
76+
env[methodName].only = env['fdescribe'];
77+
env[methodName].skip = env['xdescribe'];
78+
env[methodName].only.each = bindDescribe(originaljestFn.only.each);
79+
env[methodName].skip.each = bindDescribe(originaljestFn.skip.each);
80+
}
81+
});
82+
83+
['xit', 'fit', 'xtest', 'test', 'it'].forEach(methodName => {
84+
const originaljestFn = env[methodName];
85+
env[methodName] = function(description, specDefinitions, timeout) {
86+
arguments[1] = wrapTestInZone(specDefinitions);
87+
return originaljestFn.apply(this, arguments);
88+
};
89+
// The revised method will be populated to the final each method, so we only declare the method that in the new globals
90+
env[methodName].each = originaljestFn.each;
91+
if (methodName === 'test' || methodName === 'it') {
92+
env[methodName].only = env['fit'];
93+
env[methodName].only.each = originaljestFn.only.each;
94+
95+
env[methodName].skip = env['xit'];
96+
env[methodName].skip.each = originaljestFn.skip.each;
97+
}
98+
});
99+
100+
['beforeEach', 'afterEach', 'beforeAll', 'afterAll'].forEach(methodName => {
101+
const originaljestFn = env[methodName];
102+
env[methodName] = function(specDefinitions, timeout) {
103+
arguments[0] = wrapTestInZone(specDefinitions);
104+
return originaljestFn.apply(this, arguments);
105+
};
106+
});

0 commit comments

Comments
 (0)