diff --git a/packages/core/test/application_init_spec.ts b/packages/core/test/application_init_spec.ts index 70dc0c84af..d36e6df8fb 100644 --- a/packages/core/test/application_init_spec.ts +++ b/packages/core/test/application_init_spec.ts @@ -5,175 +5,154 @@ * Use of this source code is governed by an MIT-style license that can be * found in the LICENSE file at https://angular.io/license */ -import {APP_INITIALIZER, ApplicationInitStatus} from '@angular/core/src/application_init'; -import {Observable, Subscriber} from 'rxjs'; +import {ApplicationInitStatus} from '@angular/core/src/application_init'; +import {EMPTY, Observable, Subscriber} from 'rxjs'; -import {inject, TestBed, waitForAsync} from '../testing'; +describe('ApplicationInitStatus', () => { + let status: ApplicationInitStatus; + const runInitializers = () => + // Cast to `any` to access an internal function for testing purposes. + (status as any).runInitializers(); -{ - describe('ApplicationInitStatus', () => { - describe('no initializers', () => { - it('should return true for `done`', - waitForAsync(inject([ApplicationInitStatus], (status: ApplicationInitStatus) => { - (status as any).runInitializers(); - expect(status.done).toBe(true); - }))); - - it('should return a promise that resolves immediately for `donePromise`', - waitForAsync(inject([ApplicationInitStatus], (status: ApplicationInitStatus) => { - (status as any).runInitializers(); - status.donePromise.then(() => { - expect(status.done).toBe(true); - }); - }))); + describe('no initializers', () => { + beforeEach(() => { + status = new ApplicationInitStatus([]); }); - describe('with async promise initializers', () => { - let resolve: (result: any) => void; - let reject: (reason?: any) => void; - let promise: Promise; - let initFnInvoked = false; - beforeEach(() => { - promise = new Promise((res, rej) => { - resolve = res; - reject = rej; - }); - TestBed.configureTestingModule({ - providers: [ - {provide: APP_INITIALIZER, multi: true, useValue: () => promise}, - ] - }); - }); - - it('should update the status once all async promise initializers are done', - waitForAsync(inject([ApplicationInitStatus], (status: ApplicationInitStatus) => { - // Accessing internal `runInitializers` function of the `ApplicationInitStatus` class - // instance for testing purposes to invoke initializer functions. - (status as any).runInitializers(); - - setTimeout(() => { - initFnInvoked = true; - resolve(null); - }); - - expect(status.done).toBe(false); - status.donePromise.then(() => { - expect(status.done).toBe(true); - expect(initFnInvoked).toBe(true); - }); - }))); - - it('should handle a case when promise is rejected', - waitForAsync(inject([ApplicationInitStatus], (status: ApplicationInitStatus) => { - // Accessing internal `runInitializers` function of the `ApplicationInitStatus` class - // instance for testing purposes to invoke initializer functions. - (status as any).runInitializers(); - - setTimeout(() => { - initFnInvoked = true; - reject(); - }); - - expect(status.done).toBe(false); - status.donePromise - .then( - () => fail('`donePromise.then` should not be invoked when promise is rejected')) - .catch(() => { - expect(status.done).toBe(false); - expect(initFnInvoked).toBe(true); - }); - }))); + it('should return true for `done`', () => { + runInitializers(); + expect(status.done).toBe(true); }); - describe('with app initializers represented using observables', () => { - let subscriber: Subscriber; - let observable: Observable; - let initFnInvoked = false; - beforeEach(() => { - observable = new Observable((res) => { - subscriber = res; - }); - TestBed.configureTestingModule({ - providers: [ - {provide: APP_INITIALIZER, multi: true, useValue: () => observable}, - ] - }); - }); - - it('should update the status once all async observable initializers are completed', - waitForAsync(inject([ApplicationInitStatus], (status: ApplicationInitStatus) => { - // Accessing internal `runInitializers` function of the `ApplicationInitStatus` class - // instance for testing purposes to invoke initializer functions. - (status as any).runInitializers(); - - setTimeout(() => { - initFnInvoked = true; - subscriber.complete(); - }); - - expect(status.done).toBe(false); - status.donePromise.then(() => { - expect(status.done).toBe(true); - expect(initFnInvoked).toBe(true); - }); - }))); - - it('should update the status once all async observable initializers nexted and completed', - waitForAsync(inject([ApplicationInitStatus], (status: ApplicationInitStatus) => { - // Accessing internal `runInitializers` function of the `ApplicationInitStatus` class - // instance for testing purposes to invoke initializer functions. - (status as any).runInitializers(); - - subscriber.next('one'); - subscriber.next('two'); - - setTimeout(() => { - initFnInvoked = true; - subscriber.complete(); - }); - - expect(status.done).toBe(false); - status.donePromise.then(() => { - expect(status.done).toBe(true); - expect(initFnInvoked).toBe(true); - }); - }))); - - it('should update the status if all async observable initializers are completed before runInitializers', - waitForAsync(inject([ApplicationInitStatus], (status: ApplicationInitStatus) => { - subscriber.complete(); - // Accessing internal `runInitializers` function of the `ApplicationInitStatus` class - // instance for testing purposes to invoke initializer functions. - (status as any).runInitializers(); - - expect(status.done).toBe(false); - - status.donePromise.then(() => { - expect(status.done).toBe(true); - }); - }))); - - it('should handle a case when observable emits an error', - waitForAsync(inject([ApplicationInitStatus], (status: ApplicationInitStatus) => { - // Accessing internal `runInitializers` function of the `ApplicationInitStatus` class - // instance for testing purposes to invoke initializer functions. - (status as any).runInitializers(); - - setTimeout(() => { - initFnInvoked = true; - subscriber.error(); - }); - - expect(status.done).toBe(false); - status.donePromise - .then( - () => fail( - '`donePromise.then` should not be invoked when observable emits an error')) - .catch(() => { - expect(status.done).toBe(false); - expect(initFnInvoked).toBe(true); - }); - }))); + it('should return a promise that resolves immediately for `donePromise`', async () => { + runInitializers(); + await status.donePromise; + expect(status.done).toBe(true); }); }); -} + + describe('with async promise initializers', () => { + let resolve: (result: any) => void; + let reject: (reason?: any) => void; + let promise: Promise; + let initFnInvoked = false; + + beforeEach(() => { + promise = new Promise((res, rej) => { + resolve = res; + reject = rej; + }); + status = new ApplicationInitStatus([() => promise]); + }); + + it('should update the status once all async promise initializers are done', async () => { + runInitializers(); + + setTimeout(() => { + initFnInvoked = true; + resolve(null); + }); + + expect(status.done).toBe(false); + await status.donePromise; + expect(status.done).toBe(true); + expect(initFnInvoked).toBe(true); + }); + + it('should handle a case when promise is rejected', async () => { + runInitializers(); + + setTimeout(() => { + initFnInvoked = true; + reject(); + }); + + expect(status.done).toBe(false); + try { + await status.donePromise; + fail('donePromise should have been rejected when promise is rejected'); + } catch { + expect(status.done).toBe(false); + expect(initFnInvoked).toBe(true); + } + }); + }); + + describe('with app initializers represented using observables', () => { + let subscriber: Subscriber; + let initFnInvoked = false; + beforeEach(() => { + const observable = new Observable((res) => { + subscriber = res; + }); + status = new ApplicationInitStatus([() => observable]); + }); + + it('should update the status once all async observable initializers are completed', + async () => { + runInitializers(); + + setTimeout(() => { + initFnInvoked = true; + subscriber.complete(); + }); + + expect(status.done).toBe(false); + await status.donePromise; + expect(status.done).toBe(true); + expect(initFnInvoked).toBe(true); + }); + + it('should update the status once all async observable initializers emitted and completed', + async () => { + runInitializers(); + + subscriber.next('one'); + subscriber.next('two'); + + setTimeout(() => { + initFnInvoked = true; + subscriber.complete(); + }); + + await status.donePromise; + expect(status.done).toBe(true); + expect(initFnInvoked).toBe(true); + }); + + it('should update the status if all async observable initializers are completed synchronously', + async () => { + // Create a status instance using an initializer that returns the `EMPTY` Observable + // which completes synchronously upon subscription. + status = new ApplicationInitStatus([() => EMPTY]); + + runInitializers(); + + // Although the Observable completes synchronously, we still queue a promise for + // simplicity. This means that the `done` flag will not be `true` immediately, even + // though there was not actually any asynchronous activity. + expect(status.done).toBe(false); + + await status.donePromise; + expect(status.done).toBe(true); + }); + + it('should handle a case when observable emits an error', async () => { + runInitializers(); + + setTimeout(() => { + initFnInvoked = true; + subscriber.error(); + }); + + expect(status.done).toBe(false); + try { + await status.donePromise; + fail('donePromise should have been rejected when observable emits an error'); + } catch { + expect(status.done).toBe(false); + expect(initFnInvoked).toBe(true); + } + }); + }); +});