Skip to content
This repository was archived by the owner on Feb 26, 2024. It is now read-only.

Commit e1df4bc

Browse files
JiaLiPassionmhevery
authored andcommitted
fix(jasmine): fix #1015, make jasmine patch compatible to jasmine 3.x (#1016)
1 parent 8ee88da commit e1df4bc

10 files changed

+771
-390
lines changed

Diff for: .travis.yml

+4
Original file line numberDiff line numberDiff line change
@@ -38,3 +38,7 @@ script:
3838
- node_modules/.bin/gulp test/node
3939
- node simple-server.js 2>&1> server.log&
4040
- node ./test/webdriver/test.sauce.js
41+
42+
- npm run test:phantomjs-single
43+
- node_modules/.bin/karma start karma-dist-sauce-jasmine3.conf.js --single-run
44+
- node_modules/.bin/gulp test/node

Diff for: karma-dist-sauce-jasmine3.conf.js

+12
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
/**
2+
* @license
3+
* Copyright Google Inc. All Rights Reserved.
4+
*
5+
* Use of this source code is governed by an MIT-style license that can be
6+
* found in the LICENSE file at https://angular.io/license
7+
*/
8+
9+
module.exports = function (config) {
10+
require('./karma-dist-jasmine.conf.js')(config);
11+
require('./sauce.conf')(config, ['SL_IOS9', 'SL_CHROME', 'SL_FIREFOX_54', 'SL_SAFARI8', 'SL_SAFARI9', 'SL_SAFARI10', 'SL_IOS8', 'SL_IOS9', 'SL_IOS10', 'SL_IE9', 'SL_IE10', 'SL_IE11', 'SL_MSEDGE15', 'SL_ANDROID4.4', 'SL_ANDROID5.1'])
12+
};

Diff for: lib/jasmine/jasmine.ts

+114-50
Original file line numberDiff line numberDiff line change
@@ -9,23 +9,27 @@
99
'use strict';
1010
(() => {
1111
const __extends = function(d: any, b: any) {
12-
for (const p in b)
13-
if (b.hasOwnProperty(p)) d[p] = b[p];
12+
for (const p in b) if (b.hasOwnProperty(p)) d[p] = b[p];
1413
function __() {
1514
this.constructor = d;
1615
}
17-
d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new (__ as any)());
16+
d.prototype =
17+
b === null
18+
? Object.create(b)
19+
: ((__.prototype = b.prototype), new (__ as any)());
1820
};
1921
// Patch jasmine's describe/it/beforeEach/afterEach functions so test code always runs
2022
// in a testZone (ProxyZone). (See: angular/zone.js#91 & angular/angular#10503)
2123
if (!Zone) throw new Error('Missing: zone.js');
2224
if (typeof jasmine == 'undefined') throw new Error('Missing: jasmine.js');
2325
if ((jasmine as any)['__zone_patch__'])
24-
throw new Error('\'jasmine\' has already been patched with \'Zone\'.');
26+
throw new Error(`'jasmine' has already been patched with 'Zone'.`);
2527
(jasmine as any)['__zone_patch__'] = true;
2628

27-
const SyncTestZoneSpec: {new (name: string): ZoneSpec} = (Zone as any)['SyncTestZoneSpec'];
28-
const ProxyZoneSpec: {new (): ZoneSpec} = (Zone as any)['ProxyZoneSpec'];
29+
const SyncTestZoneSpec: { new (name: string): ZoneSpec } = (Zone as any)[
30+
'SyncTestZoneSpec'
31+
];
32+
const ProxyZoneSpec: { new (): ZoneSpec } = (Zone as any)['ProxyZoneSpec'];
2933
if (!SyncTestZoneSpec) throw new Error('Missing: SyncTestZoneSpec');
3034
if (!ProxyZoneSpec) throw new Error('Missing: ProxyZoneSpec');
3135

@@ -37,65 +41,73 @@
3741

3842
const symbol = Zone.__symbol__;
3943

40-
// This is the zone which will be used for running individual tests.
41-
// It will be a proxy zone, so that the tests function can retroactively install
42-
// different zones.
43-
// Example:
44-
// - In beforeEach() do childZone = Zone.current.fork(...);
45-
// - In it() try to do fakeAsync(). The issue is that because the beforeEach forked the
46-
// zone outside of fakeAsync it will be able to escape the fakeAsync rules.
47-
// - Because ProxyZone is parent fo `childZone` fakeAsync can retroactively add
48-
// fakeAsync behavior to the childZone.
49-
let testProxyZone: Zone = null;
50-
let testProxyZoneSpec: ZoneSpec = null;
51-
5244
// Monkey patch all of the jasmine DSL so that each function runs in appropriate zone.
5345
const jasmineEnv: any = jasmine.getEnv();
54-
['describe', 'xdescribe', 'fdescribe'].forEach((methodName) => {
46+
['describe', 'xdescribe', 'fdescribe'].forEach(methodName => {
5547
let originalJasmineFn: Function = jasmineEnv[methodName];
56-
jasmineEnv[methodName] = function(description: string, specDefinitions: Function) {
57-
return originalJasmineFn.call(this, description, wrapDescribeInZone(specDefinitions));
48+
jasmineEnv[methodName] = function(
49+
description: string,
50+
specDefinitions: Function
51+
) {
52+
return originalJasmineFn.call(
53+
this,
54+
description,
55+
wrapDescribeInZone(specDefinitions)
56+
);
5857
};
5958
});
60-
['it', 'xit', 'fit'].forEach((methodName) => {
59+
['it', 'xit', 'fit'].forEach(methodName => {
6160
let originalJasmineFn: Function = jasmineEnv[methodName];
6261
jasmineEnv[symbol(methodName)] = originalJasmineFn;
6362
jasmineEnv[methodName] = function(
64-
description: string, specDefinitions: Function, timeout: number) {
63+
description: string,
64+
specDefinitions: Function,
65+
timeout: number
66+
) {
6567
arguments[1] = wrapTestInZone(specDefinitions);
6668
return originalJasmineFn.apply(this, arguments);
6769
};
6870
});
69-
['beforeEach', 'afterEach'].forEach((methodName) => {
71+
['beforeEach', 'afterEach'].forEach(methodName => {
7072
let originalJasmineFn: Function = jasmineEnv[methodName];
7173
jasmineEnv[symbol(methodName)] = originalJasmineFn;
72-
jasmineEnv[methodName] = function(specDefinitions: Function, timeout: number) {
74+
jasmineEnv[methodName] = function(
75+
specDefinitions: Function,
76+
timeout: number
77+
) {
7378
arguments[0] = wrapTestInZone(specDefinitions);
7479
return originalJasmineFn.apply(this, arguments);
7580
};
7681
});
77-
const originalClockFn: Function = (jasmine as any)[symbol('clock')] = jasmine['clock'];
82+
const originalClockFn: Function = ((jasmine as any)[symbol('clock')] =
83+
jasmine['clock']);
7884
(jasmine as any)['clock'] = function() {
7985
const clock = originalClockFn.apply(this, arguments);
80-
const originalTick = clock[symbol('tick')] = clock.tick;
86+
const originalTick = (clock[symbol('tick')] = clock.tick);
8187
clock.tick = function() {
8288
const fakeAsyncZoneSpec = Zone.current.get('FakeAsyncTestZoneSpec');
8389
if (fakeAsyncZoneSpec) {
8490
return fakeAsyncZoneSpec.tick.apply(fakeAsyncZoneSpec, arguments);
8591
}
8692
return originalTick.apply(this, arguments);
8793
};
88-
const originalMockDate = clock[symbol('mockDate')] = clock.mockDate;
94+
const originalMockDate = (clock[symbol('mockDate')] = clock.mockDate);
8995
clock.mockDate = function() {
9096
const fakeAsyncZoneSpec = Zone.current.get('FakeAsyncTestZoneSpec');
9197
if (fakeAsyncZoneSpec) {
9298
const dateTime = arguments[0];
93-
return fakeAsyncZoneSpec.setCurrentRealTime.apply(fakeAsyncZoneSpec, dateTime && typeof dateTime.getTime === 'function' ? [dateTime.getTime()] : arguments);
99+
return fakeAsyncZoneSpec.setCurrentRealTime.apply(
100+
fakeAsyncZoneSpec,
101+
dateTime && typeof dateTime.getTime === 'function'
102+
? [dateTime.getTime()]
103+
: arguments
104+
);
94105
}
95106
return originalMockDate.apply(this, arguments);
96107
};
97108
['install', 'uninstall'].forEach(methodName => {
98-
const originalClockFn: Function = clock[symbol(methodName)] = clock[methodName];
109+
const originalClockFn: Function = (clock[symbol(methodName)] =
110+
clock[methodName]);
99111
clock[methodName] = function() {
100112
const FakeAsyncTestZoneSpec = (Zone as any)['FakeAsyncTestZoneSpec'];
101113
if (FakeAsyncTestZoneSpec) {
@@ -114,12 +126,18 @@
114126
*/
115127
function wrapDescribeInZone(describeBody: Function): Function {
116128
return function() {
117-
return syncZone.run(describeBody, this, arguments as any as any[]);
129+
return syncZone.run(describeBody, this, (arguments as any) as any[]);
118130
};
119131
}
120132

121-
function runInTestZone(testBody: Function, done?: Function) {
133+
function runInTestZone(
134+
testBody: Function,
135+
queueRunner: any,
136+
done?: Function
137+
) {
122138
const isClockInstalled = !!(jasmine as any)[symbol('clockInstalled')];
139+
const testProxyZoneSpec = queueRunner.testProxyZoneSpec;
140+
const testProxyZone = queueRunner.testProxyZone;
123141
let lastDelegate;
124142
if (isClockInstalled) {
125143
const FakeAsyncTestZoneSpec = (Zone as any)['FakeAsyncTestZoneSpec'];
@@ -151,54 +169,100 @@
151169
// The `done` callback is only passed through if the function expects at least one argument.
152170
// Note we have to make a function with correct number of arguments, otherwise jasmine will
153171
// think that all functions are sync or async.
154-
return testBody && (testBody.length ? function(done: Function) {
155-
runInTestZone(testBody, done);
156-
} : function() {
157-
runInTestZone(testBody);
158-
});
172+
return (
173+
testBody &&
174+
(testBody.length
175+
? function(done: Function) {
176+
return runInTestZone(testBody, this.queueRunner, done);
177+
}
178+
: function() {
179+
return runInTestZone(testBody, this.queueRunner);
180+
})
181+
);
159182
}
160183
interface QueueRunner {
161184
execute(): void;
162185
}
163186
interface QueueRunnerAttrs {
164-
queueableFns: {fn: Function}[];
187+
queueableFns: { fn: Function }[];
165188
onComplete: () => void;
166189
clearStack: (fn: any) => void;
167190
onException: (error: any) => void;
168191
catchException: () => boolean;
169192
userContext: any;
170-
timeout: {setTimeout: Function, clearTimeout: Function};
193+
timeout: { setTimeout: Function; clearTimeout: Function };
171194
fail: () => void;
172195
}
173196

174-
const QueueRunner = (jasmine as any).QueueRunner as {new (attrs: QueueRunnerAttrs): QueueRunner};
197+
const QueueRunner = (jasmine as any).QueueRunner as {
198+
new (attrs: QueueRunnerAttrs): QueueRunner;
199+
};
175200
(jasmine as any).QueueRunner = (function(_super) {
176201
__extends(ZoneQueueRunner, _super);
177-
function ZoneQueueRunner(attrs: {onComplete: Function}) {
178-
attrs.onComplete = ((fn) => () => {
202+
function ZoneQueueRunner(attrs: {
203+
onComplete: Function;
204+
userContext?: any;
205+
}) {
206+
attrs.onComplete = (fn => () => {
179207
// All functions are done, clear the test zone.
180-
testProxyZone = null;
181-
testProxyZoneSpec = null;
208+
this.testProxyZone = null;
209+
this.testProxyZoneSpec = null;
182210
ambientZone.scheduleMicroTask('jasmine.onComplete', fn);
183211
})(attrs.onComplete);
212+
// create a userContext to hold the queueRunner itself
213+
// so we can access the testProxy in it/xit/beforeEach ...
214+
if ((jasmine as any).UserContext) {
215+
if (!attrs.userContext) {
216+
attrs.userContext = new (jasmine as any).UserContext();
217+
}
218+
attrs.userContext.queueRunner = this;
219+
} else {
220+
if (!attrs.userContext) {
221+
attrs.userContext = {};
222+
}
223+
attrs.userContext.queueRunner = this;
224+
}
184225
_super.call(this, attrs);
185226
}
186227
ZoneQueueRunner.prototype.execute = function() {
187-
if (Zone.current !== ambientZone) throw new Error('Unexpected Zone: ' + Zone.current.name);
188-
testProxyZoneSpec = new ProxyZoneSpec();
189-
testProxyZone = ambientZone.fork(testProxyZoneSpec);
228+
let zone: Zone = Zone.current;
229+
let isChildOfAmbientZone = false;
230+
while (zone) {
231+
if (zone === ambientZone) {
232+
isChildOfAmbientZone = true;
233+
break;
234+
}
235+
zone = zone.parent;
236+
}
237+
238+
if (!isChildOfAmbientZone)
239+
throw new Error('Unexpected Zone: ' + Zone.current.name);
240+
241+
// This is the zone which will be used for running individual tests.
242+
// It will be a proxy zone, so that the tests function can retroactively install
243+
// different zones.
244+
// Example:
245+
// - In beforeEach() do childZone = Zone.current.fork(...);
246+
// - In it() try to do fakeAsync(). The issue is that because the beforeEach forked the
247+
// zone outside of fakeAsync it will be able to escape the fakeAsync rules.
248+
// - Because ProxyZone is parent fo `childZone` fakeAsync can retroactively add
249+
// fakeAsync behavior to the childZone.
250+
251+
this.testProxyZoneSpec = new ProxyZoneSpec();
252+
this.testProxyZone = ambientZone.fork(this.testProxyZoneSpec);
190253
if (!Zone.currentTask) {
191254
// if we are not running in a task then if someone would register a
192255
// element.addEventListener and then calling element.click() the
193256
// addEventListener callback would think that it is the top most task and would
194257
// drain the microtask queue on element.click() which would be incorrect.
195258
// For this reason we always force a task when running jasmine tests.
196-
Zone.current.scheduleMicroTask(
197-
'jasmine.execute().forceTask', () => QueueRunner.prototype.execute.call(this));
259+
Zone.current.scheduleMicroTask('jasmine.execute().forceTask', () =>
260+
QueueRunner.prototype.execute.call(this)
261+
);
198262
} else {
199263
_super.prototype.execute.call(this);
200264
}
201265
};
202266
return ZoneQueueRunner;
203-
}(QueueRunner));
267+
})(QueueRunner);
204268
})();

0 commit comments

Comments
 (0)