'use strict'; /** * @license Angular v * (c) 2010-2022 Google LLC. https://angular.io/ * License: MIT */ /// Zone.__load_patch('jasmine', (global, Zone, api) => { const __extends = function (d, b) { for (const p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; function __() { this.constructor = d; } d.prototype = b === null ? Object.create(b) : ((__.prototype = b.prototype), new __()); }; // Patch jasmine's describe/it/beforeEach/afterEach functions so test code always runs // in a testZone (ProxyZone). (See: angular/zone.js#91 & angular/angular#10503) if (!Zone) throw new Error('Missing: zone.js'); if (typeof jest !== 'undefined') { // return if jasmine is a light implementation inside jest // in this case, we are running inside jest not jasmine return; } if (typeof jasmine == 'undefined' || jasmine['__zone_patch__']) { return; } jasmine['__zone_patch__'] = true; const SyncTestZoneSpec = Zone['SyncTestZoneSpec']; const ProxyZoneSpec = Zone['ProxyZoneSpec']; if (!SyncTestZoneSpec) throw new Error('Missing: SyncTestZoneSpec'); if (!ProxyZoneSpec) throw new Error('Missing: ProxyZoneSpec'); const ambientZone = Zone.current; const symbol = Zone.__symbol__; // whether patch jasmine clock when in fakeAsync const disablePatchingJasmineClock = global[symbol('fakeAsyncDisablePatchingClock')] === true; // the original variable name fakeAsyncPatchLock is not accurate, so the name will be // fakeAsyncAutoFakeAsyncWhenClockPatched and if this enablePatchingJasmineClock is false, we also // automatically disable the auto jump into fakeAsync feature const enableAutoFakeAsyncWhenClockPatched = !disablePatchingJasmineClock && ((global[symbol('fakeAsyncPatchLock')] === true) || (global[symbol('fakeAsyncAutoFakeAsyncWhenClockPatched')] === true)); const ignoreUnhandledRejection = global[symbol('ignoreUnhandledRejection')] === true; if (!ignoreUnhandledRejection) { const globalErrors = jasmine.GlobalErrors; if (globalErrors && !jasmine[symbol('GlobalErrors')]) { jasmine[symbol('GlobalErrors')] = globalErrors; jasmine.GlobalErrors = function () { const instance = new globalErrors(); const originalInstall = instance.install; if (originalInstall && !instance[symbol('install')]) { instance[symbol('install')] = originalInstall; instance.install = function () { const isNode = typeof process !== 'undefined' && !!process.on; // Note: Jasmine checks internally if `process` and `process.on` is defined. Otherwise, // it installs the browser rejection handler through the `global.addEventListener`. // This code may be run in the browser environment where `process` is not defined, and // this will lead to a runtime exception since Webpack 5 removed automatic Node.js // polyfills. Note, that events are named differently, it's `unhandledRejection` in // Node.js and `unhandledrejection` in the browser. const originalHandlers = isNode ? process.listeners('unhandledRejection') : global.eventListeners('unhandledrejection'); const result = originalInstall.apply(this, arguments); isNode ? process.removeAllListeners('unhandledRejection') : global.removeAllListeners('unhandledrejection'); if (originalHandlers) { originalHandlers.forEach(handler => { if (isNode) { process.on('unhandledRejection', handler); } else { global.addEventListener('unhandledrejection', handler); } }); } return result; }; } return instance; }; } } // Monkey patch all of the jasmine DSL so that each function runs in appropriate zone. const jasmineEnv = jasmine.getEnv(); ['describe', 'xdescribe', 'fdescribe'].forEach(methodName => { let originalJasmineFn = jasmineEnv[methodName]; jasmineEnv[methodName] = function (description, specDefinitions) { return originalJasmineFn.call(this, description, wrapDescribeInZone(description, specDefinitions)); }; }); ['it', 'xit', 'fit'].forEach(methodName => { let originalJasmineFn = jasmineEnv[methodName]; jasmineEnv[symbol(methodName)] = originalJasmineFn; jasmineEnv[methodName] = function (description, specDefinitions, timeout) { arguments[1] = wrapTestInZone(specDefinitions); return originalJasmineFn.apply(this, arguments); }; }); ['beforeEach', 'afterEach', 'beforeAll', 'afterAll'].forEach(methodName => { let originalJasmineFn = jasmineEnv[methodName]; jasmineEnv[symbol(methodName)] = originalJasmineFn; jasmineEnv[methodName] = function (specDefinitions, timeout) { arguments[0] = wrapTestInZone(specDefinitions); return originalJasmineFn.apply(this, arguments); }; }); if (!disablePatchingJasmineClock) { // need to patch jasmine.clock().mockDate and jasmine.clock().tick() so // they can work properly in FakeAsyncTest const originalClockFn = (jasmine[symbol('clock')] = jasmine['clock']); jasmine['clock'] = function () { const clock = originalClockFn.apply(this, arguments); if (!clock[symbol('patched')]) { clock[symbol('patched')] = symbol('patched'); const originalTick = (clock[symbol('tick')] = clock.tick); clock.tick = function () { const fakeAsyncZoneSpec = Zone.current.get('FakeAsyncTestZoneSpec'); if (fakeAsyncZoneSpec) { return fakeAsyncZoneSpec.tick.apply(fakeAsyncZoneSpec, arguments); } return originalTick.apply(this, arguments); }; const originalMockDate = (clock[symbol('mockDate')] = clock.mockDate); clock.mockDate = function () { const fakeAsyncZoneSpec = Zone.current.get('FakeAsyncTestZoneSpec'); if (fakeAsyncZoneSpec) { const dateTime = arguments.length > 0 ? arguments[0] : new Date(); return fakeAsyncZoneSpec.setFakeBaseSystemTime.apply(fakeAsyncZoneSpec, dateTime && typeof dateTime.getTime === 'function' ? [dateTime.getTime()] : arguments); } return originalMockDate.apply(this, arguments); }; // for auto go into fakeAsync feature, we need the flag to enable it if (enableAutoFakeAsyncWhenClockPatched) { ['install', 'uninstall'].forEach(methodName => { const originalClockFn = (clock[symbol(methodName)] = clock[methodName]); clock[methodName] = function () { const FakeAsyncTestZoneSpec = Zone['FakeAsyncTestZoneSpec']; if (FakeAsyncTestZoneSpec) { jasmine[symbol('clockInstalled')] = 'install' === methodName; return; } return originalClockFn.apply(this, arguments); }; }); } } return clock; }; } // monkey patch createSpyObj to make properties enumerable to true if (!jasmine[Zone.__symbol__('createSpyObj')]) { const originalCreateSpyObj = jasmine.createSpyObj; jasmine[Zone.__symbol__('createSpyObj')] = originalCreateSpyObj; jasmine.createSpyObj = function () { const args = Array.prototype.slice.call(arguments); const propertyNames = args.length >= 3 ? args[2] : null; let spyObj; if (propertyNames) { const defineProperty = Object.defineProperty; Object.defineProperty = function (obj, p, attributes) { return defineProperty.call(this, obj, p, { ...attributes, configurable: true, enumerable: true }); }; try { spyObj = originalCreateSpyObj.apply(this, args); } finally { Object.defineProperty = defineProperty; } } else { spyObj = originalCreateSpyObj.apply(this, args); } return spyObj; }; } /** * Gets a function wrapping the body of a Jasmine `describe` block to execute in a * synchronous-only zone. */ function wrapDescribeInZone(description, describeBody) { return function () { // Create a synchronous-only zone in which to run `describe` blocks in order to raise an // error if any asynchronous operations are attempted inside of a `describe`. const syncZone = ambientZone.fork(new SyncTestZoneSpec(`jasmine.describe#${description}`)); return syncZone.run(describeBody, this, arguments); }; } function runInTestZone(testBody, applyThis, queueRunner, done) { const isClockInstalled = !!jasmine[symbol('clockInstalled')]; queueRunner.testProxyZoneSpec; const testProxyZone = queueRunner.testProxyZone; if (isClockInstalled && enableAutoFakeAsyncWhenClockPatched) { // auto run a fakeAsync const fakeAsyncModule = Zone[Zone.__symbol__('fakeAsyncTest')]; if (fakeAsyncModule && typeof fakeAsyncModule.fakeAsync === 'function') { testBody = fakeAsyncModule.fakeAsync(testBody); } } if (done) { return testProxyZone.run(testBody, applyThis, [done]); } else { return testProxyZone.run(testBody, applyThis); } } /** * Gets a function wrapping the body of a Jasmine `it/beforeEach/afterEach` block to * execute in a ProxyZone zone. * This will run in `testProxyZone`. The `testProxyZone` will be reset by the `ZoneQueueRunner` */ function wrapTestInZone(testBody) { // The `done` callback is only passed through if the function expects at least one argument. // Note we have to make a function with correct number of arguments, otherwise jasmine will // think that all functions are sync or async. return (testBody && (testBody.length ? function (done) { return runInTestZone(testBody, this, this.queueRunner, done); } : function () { return runInTestZone(testBody, this, this.queueRunner); })); } const QueueRunner = jasmine.QueueRunner; jasmine.QueueRunner = (function (_super) { __extends(ZoneQueueRunner, _super); function ZoneQueueRunner(attrs) { if (attrs.onComplete) { attrs.onComplete = (fn => () => { // All functions are done, clear the test zone. this.testProxyZone = null; this.testProxyZoneSpec = null; ambientZone.scheduleMicroTask('jasmine.onComplete', fn); })(attrs.onComplete); } const nativeSetTimeout = global[Zone.__symbol__('setTimeout')]; const nativeClearTimeout = global[Zone.__symbol__('clearTimeout')]; if (nativeSetTimeout) { // should run setTimeout inside jasmine outside of zone attrs.timeout = { setTimeout: nativeSetTimeout ? nativeSetTimeout : global.setTimeout, clearTimeout: nativeClearTimeout ? nativeClearTimeout : global.clearTimeout }; } // create a userContext to hold the queueRunner itself // so we can access the testProxy in it/xit/beforeEach ... if (jasmine.UserContext) { if (!attrs.userContext) { attrs.userContext = new jasmine.UserContext(); } attrs.userContext.queueRunner = this; } else { if (!attrs.userContext) { attrs.userContext = {}; } attrs.userContext.queueRunner = this; } // patch attrs.onException const onException = attrs.onException; attrs.onException = function (error) { if (error && error.message === 'Timeout - Async callback was not invoked within timeout specified by jasmine.DEFAULT_TIMEOUT_INTERVAL.') { // jasmine timeout, we can make the error message more // reasonable to tell what tasks are pending const proxyZoneSpec = this && this.testProxyZoneSpec; if (proxyZoneSpec) { const pendingTasksInfo = proxyZoneSpec.getAndClearPendingTasksInfo(); try { // try catch here in case error.message is not writable error.message += pendingTasksInfo; } catch (err) { } } } if (onException) { onException.call(this, error); } }; _super.call(this, attrs); } ZoneQueueRunner.prototype.execute = function () { let zone = Zone.current; let isChildOfAmbientZone = false; while (zone) { if (zone === ambientZone) { isChildOfAmbientZone = true; break; } zone = zone.parent; } if (!isChildOfAmbientZone) throw new Error('Unexpected Zone: ' + Zone.current.name); // This is the zone which will be used for running individual tests. // It will be a proxy zone, so that the tests function can retroactively install // different zones. // Example: // - In beforeEach() do childZone = Zone.current.fork(...); // - In it() try to do fakeAsync(). The issue is that because the beforeEach forked the // zone outside of fakeAsync it will be able to escape the fakeAsync rules. // - Because ProxyZone is parent fo `childZone` fakeAsync can retroactively add // fakeAsync behavior to the childZone. this.testProxyZoneSpec = new ProxyZoneSpec(); this.testProxyZone = ambientZone.fork(this.testProxyZoneSpec); if (!Zone.currentTask) { // if we are not running in a task then if someone would register a // element.addEventListener and then calling element.click() the // addEventListener callback would think that it is the top most task and would // drain the microtask queue on element.click() which would be incorrect. // For this reason we always force a task when running jasmine tests. Zone.current.scheduleMicroTask('jasmine.execute().forceTask', () => QueueRunner.prototype.execute.call(this)); } else { _super.prototype.execute.call(this); } }; return ZoneQueueRunner; })(QueueRunner); });