fix(docs-infra): notify `ErrorHandler` of `UnrecoverableState` errors (#42941)

With this commit, the `ErrorHandler` is notified of ServiceWorker
`UnrecoverableState` errors. The main purpose of this change is
gathering info about the occurrence (and frequency) of such errors in
Google analytics.

PR Close #42941
This commit is contained in:
George Kalpakas 2021-07-22 18:42:19 +03:00 committed by Dylan Hunn
parent b1082b7ad8
commit 85e93c3833
2 changed files with 35 additions and 6 deletions

View File

@ -1,4 +1,4 @@
import { ApplicationRef, Injector } from '@angular/core';
import { ApplicationRef, ErrorHandler, Injector } from '@angular/core';
import { discardPeriodicTasks, fakeAsync, tick } from '@angular/core/testing';
import { SwUpdate } from '@angular/service-worker';
import { Subject } from 'rxjs';
@ -13,6 +13,7 @@ import { SwUpdatesService } from './sw-updates.service';
describe('SwUpdatesService', () => {
let injector: Injector;
let appRef: MockApplicationRef;
let errorHandler: ErrorHandler;
let location: MockLocationService;
let service: SwUpdatesService;
let swu: MockSwUpdate;
@ -27,13 +28,15 @@ describe('SwUpdatesService', () => {
const setup = (isSwUpdateEnabled: boolean) => {
injector = Injector.create({providers: [
{ provide: ApplicationRef, useClass: MockApplicationRef, deps: [] },
{ provide: ErrorHandler, useValue: {handleError: jasmine.createSpy('handlerError')} },
{ provide: LocationService, useFactory: () => new MockLocationService(''), deps: [] },
{ provide: Logger, useClass: MockLogger, deps: [] },
{ provide: SwUpdate, useFactory: () => new MockSwUpdate(isSwUpdateEnabled), deps: [] },
{ provide: SwUpdatesService, deps: [ApplicationRef, LocationService, Logger, SwUpdate] }
{ provide: SwUpdatesService, deps: [ApplicationRef, ErrorHandler, LocationService, Logger, SwUpdate] }
]});
appRef = injector.get(ApplicationRef) as unknown as MockApplicationRef;
errorHandler = injector.get(ErrorHandler);
location = injector.get(LocationService) as unknown as MockLocationService;
service = injector.get(SwUpdatesService);
swu = injector.get(SwUpdate) as unknown as MockSwUpdate;
@ -129,6 +132,24 @@ describe('SwUpdatesService', () => {
expect(location.reloadPage).toHaveBeenCalledTimes(2);
}));
it('should notify the `ErrorHandler` when an unrecoverable state has been detected', run(() => {
expect(errorHandler.handleError).not.toHaveBeenCalled();
swu.$$unrecoverableSubj.next({reason: 'Something bad happened'});
expect(errorHandler.handleError).toHaveBeenCalledBefore(location.reloadPage);
expect(errorHandler.handleError)
.toHaveBeenCalledWith('Unrecoverable state: Something bad happened');
(errorHandler.handleError as jasmine.Spy).calls.reset();
location.reloadPage.calls.reset();
swu.$$unrecoverableSubj.next({reason: 'Something worse happened'});
expect(errorHandler.handleError).toHaveBeenCalledBefore(location.reloadPage);
expect(errorHandler.handleError)
.toHaveBeenCalledWith('Unrecoverable state: Something worse happened');
}));
describe('when `SwUpdate` is not enabled', () => {
const runDeactivated = (specFn: VoidFunction) => run(specFn, false);
@ -165,6 +186,7 @@ describe('SwUpdatesService', () => {
swu.$$unrecoverableSubj.next({reason: 'Something bad happened'});
swu.$$unrecoverableSubj.next({reason: 'Something worse happened'});
expect(errorHandler.handleError).not.toHaveBeenCalled();
expect(location.reloadPage).not.toHaveBeenCalled();
}));
});
@ -221,12 +243,15 @@ describe('SwUpdatesService', () => {
it('should stop requesting page reloads when unrecoverable states are detected', run(() => {
swu.$$unrecoverableSubj.next({reason: 'Something bad happened'});
expect(errorHandler.handleError).toHaveBeenCalledTimes(1);
expect(location.reloadPage).toHaveBeenCalledTimes(1);
service.ngOnDestroy();
(errorHandler.handleError as jasmine.Spy).calls.reset();
location.reloadPage.calls.reset();
swu.$$unrecoverableSubj.next({reason: 'Something worse happened'});
expect(errorHandler.handleError).not.toHaveBeenCalled();
expect(location.reloadPage).not.toHaveBeenCalled();
}));
});

View File

@ -1,4 +1,4 @@
import { ApplicationRef, Injectable, OnDestroy } from '@angular/core';
import { ApplicationRef, ErrorHandler, Injectable, OnDestroy } from '@angular/core';
import { SwUpdate } from '@angular/service-worker';
import { concat, interval, Subject } from 'rxjs';
import { first, takeUntil, tap } from 'rxjs/operators';
@ -21,8 +21,8 @@ export class SwUpdatesService implements OnDestroy {
private onDestroy = new Subject<void>();
constructor(
appRef: ApplicationRef, location: LocationService, private logger: Logger,
private swu: SwUpdate) {
appRef: ApplicationRef, errorHandler: ErrorHandler, location: LocationService,
private logger: Logger, private swu: SwUpdate) {
if (!swu.isEnabled) {
return;
}
@ -55,7 +55,11 @@ export class SwUpdatesService implements OnDestroy {
// Request an immediate page reload once an unrecoverable state has been detected.
this.swu.unrecoverable
.pipe(
tap(evt => this.log(`Unrecoverable state: ${evt.reason}\nReloading...`)),
tap(evt => {
const errorMsg = `Unrecoverable state: ${evt.reason}`;
errorHandler.handleError(errorMsg);
this.log(`${errorMsg}\nReloading...`);
}),
takeUntil(this.onDestroy),
)
.subscribe(() => location.reloadPage());