[6a3a178] | 1 | 'use strict';
|
---|
| 2 | /**
|
---|
| 3 | * @license Angular v12.0.0-next.0
|
---|
| 4 | * (c) 2010-2020 Google LLC. https://angular.io/
|
---|
| 5 | * License: MIT
|
---|
| 6 | */
|
---|
| 7 | /**
|
---|
| 8 | * @license
|
---|
| 9 | * Copyright Google LLC All Rights Reserved.
|
---|
| 10 | *
|
---|
| 11 | * Use of this source code is governed by an MIT-style license that can be
|
---|
| 12 | * found in the LICENSE file at https://angular.io/license
|
---|
| 13 | */
|
---|
| 14 | Zone.__load_patch('jasmine', (global, Zone, api) => {
|
---|
| 15 | const __extends = function (d, b) {
|
---|
| 16 | for (const p in b)
|
---|
| 17 | if (b.hasOwnProperty(p))
|
---|
| 18 | d[p] = b[p];
|
---|
| 19 | function __() {
|
---|
| 20 | this.constructor = d;
|
---|
| 21 | }
|
---|
| 22 | d.prototype = b === null ? Object.create(b) : ((__.prototype = b.prototype), new __());
|
---|
| 23 | };
|
---|
| 24 | // Patch jasmine's describe/it/beforeEach/afterEach functions so test code always runs
|
---|
| 25 | // in a testZone (ProxyZone). (See: angular/zone.js#91 & angular/angular#10503)
|
---|
| 26 | if (!Zone)
|
---|
| 27 | throw new Error('Missing: zone.js');
|
---|
| 28 | if (typeof jest !== 'undefined') {
|
---|
| 29 | // return if jasmine is a light implementation inside jest
|
---|
| 30 | // in this case, we are running inside jest not jasmine
|
---|
| 31 | return;
|
---|
| 32 | }
|
---|
| 33 | if (typeof jasmine == 'undefined' || jasmine['__zone_patch__']) {
|
---|
| 34 | return;
|
---|
| 35 | }
|
---|
| 36 | jasmine['__zone_patch__'] = true;
|
---|
| 37 | const SyncTestZoneSpec = Zone['SyncTestZoneSpec'];
|
---|
| 38 | const ProxyZoneSpec = Zone['ProxyZoneSpec'];
|
---|
| 39 | if (!SyncTestZoneSpec)
|
---|
| 40 | throw new Error('Missing: SyncTestZoneSpec');
|
---|
| 41 | if (!ProxyZoneSpec)
|
---|
| 42 | throw new Error('Missing: ProxyZoneSpec');
|
---|
| 43 | const ambientZone = Zone.current;
|
---|
| 44 | // Create a synchronous-only zone in which to run `describe` blocks in order to raise an
|
---|
| 45 | // error if any asynchronous operations are attempted inside of a `describe` but outside of
|
---|
| 46 | // a `beforeEach` or `it`.
|
---|
| 47 | const syncZone = ambientZone.fork(new SyncTestZoneSpec('jasmine.describe'));
|
---|
| 48 | const symbol = Zone.__symbol__;
|
---|
| 49 | // whether patch jasmine clock when in fakeAsync
|
---|
| 50 | const disablePatchingJasmineClock = global[symbol('fakeAsyncDisablePatchingClock')] === true;
|
---|
| 51 | // the original variable name fakeAsyncPatchLock is not accurate, so the name will be
|
---|
| 52 | // fakeAsyncAutoFakeAsyncWhenClockPatched and if this enablePatchingJasmineClock is false, we also
|
---|
| 53 | // automatically disable the auto jump into fakeAsync feature
|
---|
| 54 | const enableAutoFakeAsyncWhenClockPatched = !disablePatchingJasmineClock &&
|
---|
| 55 | ((global[symbol('fakeAsyncPatchLock')] === true) ||
|
---|
| 56 | (global[symbol('fakeAsyncAutoFakeAsyncWhenClockPatched')] === true));
|
---|
| 57 | const ignoreUnhandledRejection = global[symbol('ignoreUnhandledRejection')] === true;
|
---|
| 58 | if (!ignoreUnhandledRejection) {
|
---|
| 59 | const globalErrors = jasmine.GlobalErrors;
|
---|
| 60 | if (globalErrors && !jasmine[symbol('GlobalErrors')]) {
|
---|
| 61 | jasmine[symbol('GlobalErrors')] = globalErrors;
|
---|
| 62 | jasmine.GlobalErrors = function () {
|
---|
| 63 | const instance = new globalErrors();
|
---|
| 64 | const originalInstall = instance.install;
|
---|
| 65 | if (originalInstall && !instance[symbol('install')]) {
|
---|
| 66 | instance[symbol('install')] = originalInstall;
|
---|
| 67 | instance.install = function () {
|
---|
| 68 | const originalHandlers = process.listeners('unhandledRejection');
|
---|
| 69 | const r = originalInstall.apply(this, arguments);
|
---|
| 70 | process.removeAllListeners('unhandledRejection');
|
---|
| 71 | if (originalHandlers) {
|
---|
| 72 | originalHandlers.forEach(h => process.on('unhandledRejection', h));
|
---|
| 73 | }
|
---|
| 74 | return r;
|
---|
| 75 | };
|
---|
| 76 | }
|
---|
| 77 | return instance;
|
---|
| 78 | };
|
---|
| 79 | }
|
---|
| 80 | }
|
---|
| 81 | // Monkey patch all of the jasmine DSL so that each function runs in appropriate zone.
|
---|
| 82 | const jasmineEnv = jasmine.getEnv();
|
---|
| 83 | ['describe', 'xdescribe', 'fdescribe'].forEach(methodName => {
|
---|
| 84 | let originalJasmineFn = jasmineEnv[methodName];
|
---|
| 85 | jasmineEnv[methodName] = function (description, specDefinitions) {
|
---|
| 86 | return originalJasmineFn.call(this, description, wrapDescribeInZone(specDefinitions));
|
---|
| 87 | };
|
---|
| 88 | });
|
---|
| 89 | ['it', 'xit', 'fit'].forEach(methodName => {
|
---|
| 90 | let originalJasmineFn = jasmineEnv[methodName];
|
---|
| 91 | jasmineEnv[symbol(methodName)] = originalJasmineFn;
|
---|
| 92 | jasmineEnv[methodName] = function (description, specDefinitions, timeout) {
|
---|
| 93 | arguments[1] = wrapTestInZone(specDefinitions);
|
---|
| 94 | return originalJasmineFn.apply(this, arguments);
|
---|
| 95 | };
|
---|
| 96 | });
|
---|
| 97 | ['beforeEach', 'afterEach', 'beforeAll', 'afterAll'].forEach(methodName => {
|
---|
| 98 | let originalJasmineFn = jasmineEnv[methodName];
|
---|
| 99 | jasmineEnv[symbol(methodName)] = originalJasmineFn;
|
---|
| 100 | jasmineEnv[methodName] = function (specDefinitions, timeout) {
|
---|
| 101 | arguments[0] = wrapTestInZone(specDefinitions);
|
---|
| 102 | return originalJasmineFn.apply(this, arguments);
|
---|
| 103 | };
|
---|
| 104 | });
|
---|
| 105 | if (!disablePatchingJasmineClock) {
|
---|
| 106 | // need to patch jasmine.clock().mockDate and jasmine.clock().tick() so
|
---|
| 107 | // they can work properly in FakeAsyncTest
|
---|
| 108 | const originalClockFn = (jasmine[symbol('clock')] = jasmine['clock']);
|
---|
| 109 | jasmine['clock'] = function () {
|
---|
| 110 | const clock = originalClockFn.apply(this, arguments);
|
---|
| 111 | if (!clock[symbol('patched')]) {
|
---|
| 112 | clock[symbol('patched')] = symbol('patched');
|
---|
| 113 | const originalTick = (clock[symbol('tick')] = clock.tick);
|
---|
| 114 | clock.tick = function () {
|
---|
| 115 | const fakeAsyncZoneSpec = Zone.current.get('FakeAsyncTestZoneSpec');
|
---|
| 116 | if (fakeAsyncZoneSpec) {
|
---|
| 117 | return fakeAsyncZoneSpec.tick.apply(fakeAsyncZoneSpec, arguments);
|
---|
| 118 | }
|
---|
| 119 | return originalTick.apply(this, arguments);
|
---|
| 120 | };
|
---|
| 121 | const originalMockDate = (clock[symbol('mockDate')] = clock.mockDate);
|
---|
| 122 | clock.mockDate = function () {
|
---|
| 123 | const fakeAsyncZoneSpec = Zone.current.get('FakeAsyncTestZoneSpec');
|
---|
| 124 | if (fakeAsyncZoneSpec) {
|
---|
| 125 | const dateTime = arguments.length > 0 ? arguments[0] : new Date();
|
---|
| 126 | return fakeAsyncZoneSpec.setFakeBaseSystemTime.apply(fakeAsyncZoneSpec, dateTime && typeof dateTime.getTime === 'function' ? [dateTime.getTime()] :
|
---|
| 127 | arguments);
|
---|
| 128 | }
|
---|
| 129 | return originalMockDate.apply(this, arguments);
|
---|
| 130 | };
|
---|
| 131 | // for auto go into fakeAsync feature, we need the flag to enable it
|
---|
| 132 | if (enableAutoFakeAsyncWhenClockPatched) {
|
---|
| 133 | ['install', 'uninstall'].forEach(methodName => {
|
---|
| 134 | const originalClockFn = (clock[symbol(methodName)] = clock[methodName]);
|
---|
| 135 | clock[methodName] = function () {
|
---|
| 136 | const FakeAsyncTestZoneSpec = Zone['FakeAsyncTestZoneSpec'];
|
---|
| 137 | if (FakeAsyncTestZoneSpec) {
|
---|
| 138 | jasmine[symbol('clockInstalled')] = 'install' === methodName;
|
---|
| 139 | return;
|
---|
| 140 | }
|
---|
| 141 | return originalClockFn.apply(this, arguments);
|
---|
| 142 | };
|
---|
| 143 | });
|
---|
| 144 | }
|
---|
| 145 | }
|
---|
| 146 | return clock;
|
---|
| 147 | };
|
---|
| 148 | }
|
---|
| 149 | // monkey patch createSpyObj to make properties enumerable to true
|
---|
| 150 | if (!jasmine[Zone.__symbol__('createSpyObj')]) {
|
---|
| 151 | const originalCreateSpyObj = jasmine.createSpyObj;
|
---|
| 152 | jasmine[Zone.__symbol__('createSpyObj')] = originalCreateSpyObj;
|
---|
| 153 | jasmine.createSpyObj = function () {
|
---|
| 154 | const args = Array.prototype.slice.call(arguments);
|
---|
| 155 | const propertyNames = args.length >= 3 ? args[2] : null;
|
---|
| 156 | let spyObj;
|
---|
| 157 | if (propertyNames) {
|
---|
| 158 | const defineProperty = Object.defineProperty;
|
---|
| 159 | Object.defineProperty = function (obj, p, attributes) {
|
---|
| 160 | return defineProperty.call(this, obj, p, Object.assign(Object.assign({}, attributes), { configurable: true, enumerable: true }));
|
---|
| 161 | };
|
---|
| 162 | try {
|
---|
| 163 | spyObj = originalCreateSpyObj.apply(this, args);
|
---|
| 164 | }
|
---|
| 165 | finally {
|
---|
| 166 | Object.defineProperty = defineProperty;
|
---|
| 167 | }
|
---|
| 168 | }
|
---|
| 169 | else {
|
---|
| 170 | spyObj = originalCreateSpyObj.apply(this, args);
|
---|
| 171 | }
|
---|
| 172 | return spyObj;
|
---|
| 173 | };
|
---|
| 174 | }
|
---|
| 175 | /**
|
---|
| 176 | * Gets a function wrapping the body of a Jasmine `describe` block to execute in a
|
---|
| 177 | * synchronous-only zone.
|
---|
| 178 | */
|
---|
| 179 | function wrapDescribeInZone(describeBody) {
|
---|
| 180 | return function () {
|
---|
| 181 | return syncZone.run(describeBody, this, arguments);
|
---|
| 182 | };
|
---|
| 183 | }
|
---|
| 184 | function runInTestZone(testBody, applyThis, queueRunner, done) {
|
---|
| 185 | const isClockInstalled = !!jasmine[symbol('clockInstalled')];
|
---|
| 186 | const testProxyZoneSpec = queueRunner.testProxyZoneSpec;
|
---|
| 187 | const testProxyZone = queueRunner.testProxyZone;
|
---|
| 188 | if (isClockInstalled && enableAutoFakeAsyncWhenClockPatched) {
|
---|
| 189 | // auto run a fakeAsync
|
---|
| 190 | const fakeAsyncModule = Zone[Zone.__symbol__('fakeAsyncTest')];
|
---|
| 191 | if (fakeAsyncModule && typeof fakeAsyncModule.fakeAsync === 'function') {
|
---|
| 192 | testBody = fakeAsyncModule.fakeAsync(testBody);
|
---|
| 193 | }
|
---|
| 194 | }
|
---|
| 195 | if (done) {
|
---|
| 196 | return testProxyZone.run(testBody, applyThis, [done]);
|
---|
| 197 | }
|
---|
| 198 | else {
|
---|
| 199 | return testProxyZone.run(testBody, applyThis);
|
---|
| 200 | }
|
---|
| 201 | }
|
---|
| 202 | /**
|
---|
| 203 | * Gets a function wrapping the body of a Jasmine `it/beforeEach/afterEach` block to
|
---|
| 204 | * execute in a ProxyZone zone.
|
---|
| 205 | * This will run in `testProxyZone`. The `testProxyZone` will be reset by the `ZoneQueueRunner`
|
---|
| 206 | */
|
---|
| 207 | function wrapTestInZone(testBody) {
|
---|
| 208 | // The `done` callback is only passed through if the function expects at least one argument.
|
---|
| 209 | // Note we have to make a function with correct number of arguments, otherwise jasmine will
|
---|
| 210 | // think that all functions are sync or async.
|
---|
| 211 | return (testBody && (testBody.length ? function (done) {
|
---|
| 212 | return runInTestZone(testBody, this, this.queueRunner, done);
|
---|
| 213 | } : function () {
|
---|
| 214 | return runInTestZone(testBody, this, this.queueRunner);
|
---|
| 215 | }));
|
---|
| 216 | }
|
---|
| 217 | const QueueRunner = jasmine.QueueRunner;
|
---|
| 218 | jasmine.QueueRunner = (function (_super) {
|
---|
| 219 | __extends(ZoneQueueRunner, _super);
|
---|
| 220 | function ZoneQueueRunner(attrs) {
|
---|
| 221 | if (attrs.onComplete) {
|
---|
| 222 | attrs.onComplete = (fn => () => {
|
---|
| 223 | // All functions are done, clear the test zone.
|
---|
| 224 | this.testProxyZone = null;
|
---|
| 225 | this.testProxyZoneSpec = null;
|
---|
| 226 | ambientZone.scheduleMicroTask('jasmine.onComplete', fn);
|
---|
| 227 | })(attrs.onComplete);
|
---|
| 228 | }
|
---|
| 229 | const nativeSetTimeout = global[Zone.__symbol__('setTimeout')];
|
---|
| 230 | const nativeClearTimeout = global[Zone.__symbol__('clearTimeout')];
|
---|
| 231 | if (nativeSetTimeout) {
|
---|
| 232 | // should run setTimeout inside jasmine outside of zone
|
---|
| 233 | attrs.timeout = {
|
---|
| 234 | setTimeout: nativeSetTimeout ? nativeSetTimeout : global.setTimeout,
|
---|
| 235 | clearTimeout: nativeClearTimeout ? nativeClearTimeout : global.clearTimeout
|
---|
| 236 | };
|
---|
| 237 | }
|
---|
| 238 | // create a userContext to hold the queueRunner itself
|
---|
| 239 | // so we can access the testProxy in it/xit/beforeEach ...
|
---|
| 240 | if (jasmine.UserContext) {
|
---|
| 241 | if (!attrs.userContext) {
|
---|
| 242 | attrs.userContext = new jasmine.UserContext();
|
---|
| 243 | }
|
---|
| 244 | attrs.userContext.queueRunner = this;
|
---|
| 245 | }
|
---|
| 246 | else {
|
---|
| 247 | if (!attrs.userContext) {
|
---|
| 248 | attrs.userContext = {};
|
---|
| 249 | }
|
---|
| 250 | attrs.userContext.queueRunner = this;
|
---|
| 251 | }
|
---|
| 252 | // patch attrs.onException
|
---|
| 253 | const onException = attrs.onException;
|
---|
| 254 | attrs.onException = function (error) {
|
---|
| 255 | if (error &&
|
---|
| 256 | error.message ===
|
---|
| 257 | 'Timeout - Async callback was not invoked within timeout specified by jasmine.DEFAULT_TIMEOUT_INTERVAL.') {
|
---|
| 258 | // jasmine timeout, we can make the error message more
|
---|
| 259 | // reasonable to tell what tasks are pending
|
---|
| 260 | const proxyZoneSpec = this && this.testProxyZoneSpec;
|
---|
| 261 | if (proxyZoneSpec) {
|
---|
| 262 | const pendingTasksInfo = proxyZoneSpec.getAndClearPendingTasksInfo();
|
---|
| 263 | try {
|
---|
| 264 | // try catch here in case error.message is not writable
|
---|
| 265 | error.message += pendingTasksInfo;
|
---|
| 266 | }
|
---|
| 267 | catch (err) {
|
---|
| 268 | }
|
---|
| 269 | }
|
---|
| 270 | }
|
---|
| 271 | if (onException) {
|
---|
| 272 | onException.call(this, error);
|
---|
| 273 | }
|
---|
| 274 | };
|
---|
| 275 | _super.call(this, attrs);
|
---|
| 276 | }
|
---|
| 277 | ZoneQueueRunner.prototype.execute = function () {
|
---|
| 278 | let zone = Zone.current;
|
---|
| 279 | let isChildOfAmbientZone = false;
|
---|
| 280 | while (zone) {
|
---|
| 281 | if (zone === ambientZone) {
|
---|
| 282 | isChildOfAmbientZone = true;
|
---|
| 283 | break;
|
---|
| 284 | }
|
---|
| 285 | zone = zone.parent;
|
---|
| 286 | }
|
---|
| 287 | if (!isChildOfAmbientZone)
|
---|
| 288 | throw new Error('Unexpected Zone: ' + Zone.current.name);
|
---|
| 289 | // This is the zone which will be used for running individual tests.
|
---|
| 290 | // It will be a proxy zone, so that the tests function can retroactively install
|
---|
| 291 | // different zones.
|
---|
| 292 | // Example:
|
---|
| 293 | // - In beforeEach() do childZone = Zone.current.fork(...);
|
---|
| 294 | // - In it() try to do fakeAsync(). The issue is that because the beforeEach forked the
|
---|
| 295 | // zone outside of fakeAsync it will be able to escape the fakeAsync rules.
|
---|
| 296 | // - Because ProxyZone is parent fo `childZone` fakeAsync can retroactively add
|
---|
| 297 | // fakeAsync behavior to the childZone.
|
---|
| 298 | this.testProxyZoneSpec = new ProxyZoneSpec();
|
---|
| 299 | this.testProxyZone = ambientZone.fork(this.testProxyZoneSpec);
|
---|
| 300 | if (!Zone.currentTask) {
|
---|
| 301 | // if we are not running in a task then if someone would register a
|
---|
| 302 | // element.addEventListener and then calling element.click() the
|
---|
| 303 | // addEventListener callback would think that it is the top most task and would
|
---|
| 304 | // drain the microtask queue on element.click() which would be incorrect.
|
---|
| 305 | // For this reason we always force a task when running jasmine tests.
|
---|
| 306 | Zone.current.scheduleMicroTask('jasmine.execute().forceTask', () => QueueRunner.prototype.execute.call(this));
|
---|
| 307 | }
|
---|
| 308 | else {
|
---|
| 309 | _super.prototype.execute.call(this);
|
---|
| 310 | }
|
---|
| 311 | };
|
---|
| 312 | return ZoneQueueRunner;
|
---|
| 313 | })(QueueRunner);
|
---|
| 314 | });
|
---|