|
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