|
9 | 9 | 'use strict';
|
10 | 10 | (() => {
|
11 | 11 | 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]; |
14 | 13 | function __() {
|
15 | 14 | this.constructor = d;
|
16 | 15 | }
|
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)()); |
18 | 20 | };
|
19 | 21 | // Patch jasmine's describe/it/beforeEach/afterEach functions so test code always runs
|
20 | 22 | // in a testZone (ProxyZone). (See: angular/zone.js#91 & angular/angular#10503)
|
21 | 23 | if (!Zone) throw new Error('Missing: zone.js');
|
22 | 24 | if (typeof jasmine == 'undefined') throw new Error('Missing: jasmine.js');
|
23 | 25 | 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'.`); |
25 | 27 | (jasmine as any)['__zone_patch__'] = true;
|
26 | 28 |
|
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']; |
29 | 33 | if (!SyncTestZoneSpec) throw new Error('Missing: SyncTestZoneSpec');
|
30 | 34 | if (!ProxyZoneSpec) throw new Error('Missing: ProxyZoneSpec');
|
31 | 35 |
|
|
37 | 41 |
|
38 | 42 | const symbol = Zone.__symbol__;
|
39 | 43 |
|
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 |
| - |
52 | 44 | // Monkey patch all of the jasmine DSL so that each function runs in appropriate zone.
|
53 | 45 | const jasmineEnv: any = jasmine.getEnv();
|
54 |
| - ['describe', 'xdescribe', 'fdescribe'].forEach((methodName) => { |
| 46 | + ['describe', 'xdescribe', 'fdescribe'].forEach(methodName => { |
55 | 47 | 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 | + ); |
58 | 57 | };
|
59 | 58 | });
|
60 |
| - ['it', 'xit', 'fit'].forEach((methodName) => { |
| 59 | + ['it', 'xit', 'fit'].forEach(methodName => { |
61 | 60 | let originalJasmineFn: Function = jasmineEnv[methodName];
|
62 | 61 | jasmineEnv[symbol(methodName)] = originalJasmineFn;
|
63 | 62 | jasmineEnv[methodName] = function(
|
64 |
| - description: string, specDefinitions: Function, timeout: number) { |
| 63 | + description: string, |
| 64 | + specDefinitions: Function, |
| 65 | + timeout: number |
| 66 | + ) { |
65 | 67 | arguments[1] = wrapTestInZone(specDefinitions);
|
66 | 68 | return originalJasmineFn.apply(this, arguments);
|
67 | 69 | };
|
68 | 70 | });
|
69 |
| - ['beforeEach', 'afterEach'].forEach((methodName) => { |
| 71 | + ['beforeEach', 'afterEach'].forEach(methodName => { |
70 | 72 | let originalJasmineFn: Function = jasmineEnv[methodName];
|
71 | 73 | jasmineEnv[symbol(methodName)] = originalJasmineFn;
|
72 |
| - jasmineEnv[methodName] = function(specDefinitions: Function, timeout: number) { |
| 74 | + jasmineEnv[methodName] = function( |
| 75 | + specDefinitions: Function, |
| 76 | + timeout: number |
| 77 | + ) { |
73 | 78 | arguments[0] = wrapTestInZone(specDefinitions);
|
74 | 79 | return originalJasmineFn.apply(this, arguments);
|
75 | 80 | };
|
76 | 81 | });
|
77 |
| - const originalClockFn: Function = (jasmine as any)[symbol('clock')] = jasmine['clock']; |
| 82 | + const originalClockFn: Function = ((jasmine as any)[symbol('clock')] = |
| 83 | + jasmine['clock']); |
78 | 84 | (jasmine as any)['clock'] = function() {
|
79 | 85 | const clock = originalClockFn.apply(this, arguments);
|
80 |
| - const originalTick = clock[symbol('tick')] = clock.tick; |
| 86 | + const originalTick = (clock[symbol('tick')] = clock.tick); |
81 | 87 | clock.tick = function() {
|
82 | 88 | const fakeAsyncZoneSpec = Zone.current.get('FakeAsyncTestZoneSpec');
|
83 | 89 | if (fakeAsyncZoneSpec) {
|
84 | 90 | return fakeAsyncZoneSpec.tick.apply(fakeAsyncZoneSpec, arguments);
|
85 | 91 | }
|
86 | 92 | return originalTick.apply(this, arguments);
|
87 | 93 | };
|
88 |
| - const originalMockDate = clock[symbol('mockDate')] = clock.mockDate; |
| 94 | + const originalMockDate = (clock[symbol('mockDate')] = clock.mockDate); |
89 | 95 | clock.mockDate = function() {
|
90 | 96 | const fakeAsyncZoneSpec = Zone.current.get('FakeAsyncTestZoneSpec');
|
91 | 97 | if (fakeAsyncZoneSpec) {
|
92 | 98 | 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 | + ); |
94 | 105 | }
|
95 | 106 | return originalMockDate.apply(this, arguments);
|
96 | 107 | };
|
97 | 108 | ['install', 'uninstall'].forEach(methodName => {
|
98 |
| - const originalClockFn: Function = clock[symbol(methodName)] = clock[methodName]; |
| 109 | + const originalClockFn: Function = (clock[symbol(methodName)] = |
| 110 | + clock[methodName]); |
99 | 111 | clock[methodName] = function() {
|
100 | 112 | const FakeAsyncTestZoneSpec = (Zone as any)['FakeAsyncTestZoneSpec'];
|
101 | 113 | if (FakeAsyncTestZoneSpec) {
|
|
114 | 126 | */
|
115 | 127 | function wrapDescribeInZone(describeBody: Function): Function {
|
116 | 128 | return function() {
|
117 |
| - return syncZone.run(describeBody, this, arguments as any as any[]); |
| 129 | + return syncZone.run(describeBody, this, (arguments as any) as any[]); |
118 | 130 | };
|
119 | 131 | }
|
120 | 132 |
|
121 |
| - function runInTestZone(testBody: Function, done?: Function) { |
| 133 | + function runInTestZone( |
| 134 | + testBody: Function, |
| 135 | + queueRunner: any, |
| 136 | + done?: Function |
| 137 | + ) { |
122 | 138 | const isClockInstalled = !!(jasmine as any)[symbol('clockInstalled')];
|
| 139 | + const testProxyZoneSpec = queueRunner.testProxyZoneSpec; |
| 140 | + const testProxyZone = queueRunner.testProxyZone; |
123 | 141 | let lastDelegate;
|
124 | 142 | if (isClockInstalled) {
|
125 | 143 | const FakeAsyncTestZoneSpec = (Zone as any)['FakeAsyncTestZoneSpec'];
|
|
151 | 169 | // The `done` callback is only passed through if the function expects at least one argument.
|
152 | 170 | // Note we have to make a function with correct number of arguments, otherwise jasmine will
|
153 | 171 | // 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 | + ); |
159 | 182 | }
|
160 | 183 | interface QueueRunner {
|
161 | 184 | execute(): void;
|
162 | 185 | }
|
163 | 186 | interface QueueRunnerAttrs {
|
164 |
| - queueableFns: {fn: Function}[]; |
| 187 | + queueableFns: { fn: Function }[]; |
165 | 188 | onComplete: () => void;
|
166 | 189 | clearStack: (fn: any) => void;
|
167 | 190 | onException: (error: any) => void;
|
168 | 191 | catchException: () => boolean;
|
169 | 192 | userContext: any;
|
170 |
| - timeout: {setTimeout: Function, clearTimeout: Function}; |
| 193 | + timeout: { setTimeout: Function; clearTimeout: Function }; |
171 | 194 | fail: () => void;
|
172 | 195 | }
|
173 | 196 |
|
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 | + }; |
175 | 200 | (jasmine as any).QueueRunner = (function(_super) {
|
176 | 201 | __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 => () => { |
179 | 207 | // All functions are done, clear the test zone.
|
180 |
| - testProxyZone = null; |
181 |
| - testProxyZoneSpec = null; |
| 208 | + this.testProxyZone = null; |
| 209 | + this.testProxyZoneSpec = null; |
182 | 210 | ambientZone.scheduleMicroTask('jasmine.onComplete', fn);
|
183 | 211 | })(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 | + } |
184 | 225 | _super.call(this, attrs);
|
185 | 226 | }
|
186 | 227 | 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); |
190 | 253 | if (!Zone.currentTask) {
|
191 | 254 | // if we are not running in a task then if someone would register a
|
192 | 255 | // element.addEventListener and then calling element.click() the
|
193 | 256 | // addEventListener callback would think that it is the top most task and would
|
194 | 257 | // drain the microtask queue on element.click() which would be incorrect.
|
195 | 258 | // 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 | + ); |
198 | 262 | } else {
|
199 | 263 | _super.prototype.execute.call(this);
|
200 | 264 | }
|
201 | 265 | };
|
202 | 266 | return ZoneQueueRunner;
|
203 |
| - }(QueueRunner)); |
| 267 | + })(QueueRunner); |
204 | 268 | })();
|
0 commit comments