angular-docs-cn/packages/core/test/application_init_spec.ts
Andrew Kushnir 995adb2297 test(core): refactor ApplicationInitStatus tests to avoid TestBed side-effects (#33222)
Currently TestBed (both ViewEngine and Ivy) invoke `ApplicationInitStatus.runInitializers` as a part of the
bootstrap process to mimic real bootstrap steps. This is problematic for the `ApplicationInitStatus` class
tests since the `runInitializers` call performed by TestBed interfere with actual tests.

This commit updates ApplicationInitStatus tests to interact with the class directly instead of relying on TestBed
APIs to retrieve the class though DI.

PR Close #33222
2021-02-22 08:41:49 -08:00

159 lines
4.5 KiB
TypeScript

/**
* @license
* Copyright Google LLC All Rights Reserved.
*
* 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 {ApplicationInitStatus} from '@angular/core/src/application_init';
import {EMPTY, Observable, Subscriber} from 'rxjs';
describe('ApplicationInitStatus', () => {
let status: ApplicationInitStatus;
const runInitializers = () =>
// Cast to `any` to access an internal function for testing purposes.
(status as any).runInitializers();
describe('no initializers', () => {
beforeEach(() => {
status = new ApplicationInitStatus([]);
});
it('should return true for `done`', () => {
runInitializers();
expect(status.done).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<any>;
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<any>;
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);
}
});
});
});