@angular/core#ErrorHandler TypeScript Examples

The following examples show how to use @angular/core#ErrorHandler. You can vote up the ones you like or vote down the ones you don't like, and go to the original project or source file by following the links above each example. You may check out the related API usage on the sidebar.
Example #1
Source File: app.module.ts    From leapp with Mozilla Public License 2.0 6 votes vote down vote up
@NgModule({
  declarations: [AppComponent, TrayMenuComponent],
  imports: [
    ComponentsModule,
    MatSnackBarModule,
    BrowserModule,
    BrowserAnimationsModule,
    AppRoutingModule,
    HttpClientModule,
    FormsModule,
    ReactiveFormsModule,
    NgSelectModule,
    LayoutModule,
    TooltipModule.forRoot(),
    ModalModule.forRoot(),
    ToastrModule.forRoot(),
  ],
  entryComponents: [ConfirmationDialogComponent, InputDialogComponent],
  providers: [{ provide: ErrorHandler, useClass: ErrorService }],
  bootstrap: [AppComponent],
})
export class AppModule {}
Example #2
Source File: mini-program.module.ts    From angular-miniprogram with MIT License 6 votes vote down vote up
@NgModule({
  declarations: [],
  imports: [CommonModule, ApplicationModule],
  providers: [
    { provide: ɵINJECTOR_SCOPE, useValue: 'root' },
    { provide: ErrorHandler, useFactory: errorHandler, deps: [] },
    MiniProgramRendererFactory,
    {
      provide: RendererFactory2,
      useExisting: MiniProgramRendererFactory,
    },
    PageService,
    ComponentFinderService,
  ],
  exports: [CommonModule, ApplicationModule],
})
export class MiniProgramModule {
  constructor(pageService: PageService) {
    pageService.register();
  }
}
Example #3
Source File: error-handler.service.ts    From matx-angular with MIT License 6 votes vote down vote up
@Injectable()
export class ErrorHandlerService extends ErrorHandler {

    errorCount = 0;

    constructor(protected injector: Injector) {
        super();
    }
    // https://github.com/angular/angular/issues/17010
    handleError(error: any) {
        let increment = 5;
        let max = 50;

        // Prevents change detection
        let debugCtx = error['ngDebugContext'];
        let changeDetectorRef = debugCtx && debugCtx.injector.get(ChangeDetectorRef);
        if (changeDetectorRef) changeDetectorRef.detach();

        this.errorCount = this.errorCount + 1;
        if (this.errorCount % increment === 0) {
            console.log(' ');
            console.log(`errorHandler() was called ${this.errorCount} times.`);
            console.log(' ');
            super.handleError(error);

            if (this.errorCount === max) {
                console.log(' ');
                console.log(`Preventing recursive error after ${this.errorCount} recursive errors.`);
                console.log(' ');

                let appRef = this.injector.get(ApplicationRef);
                appRef.tick();
            }
        }
        else if (this.errorCount === 1) {
            super.handleError(error);
        }
    }
}
Example #4
Source File: app.module.ts    From matx-angular with MIT License 6 votes vote down vote up
@NgModule({
  imports: [
    BrowserModule,
    BrowserAnimationsModule,
    SharedModule,
    HttpClientModule,
    PerfectScrollbarModule,

    InMemoryWebApiModule.forRoot(InMemoryDataService, { passThruUnknownUrl: true}),
    RouterModule.forRoot(rootRouterConfig, { useHash: false })
  ],
  declarations: [AppComponent],
  providers: [
    { provide: ErrorHandler, useClass: ErrorHandlerService },
    { provide: HAMMER_GESTURE_CONFIG, useClass: GestureConfig },
    { provide: PERFECT_SCROLLBAR_CONFIG, useValue: DEFAULT_PERFECT_SCROLLBAR_CONFIG },
    
    // REQUIRED IF YOU USE JWT AUTHENTICATION
    {
      provide: HTTP_INTERCEPTORS,
      useClass: TokenInterceptor,
      multi: true,
    },
  ],
  bootstrap: [AppComponent]
})
export class AppModule { }
Example #5
Source File: app.module.ts    From jira-clone-angular with MIT License 6 votes vote down vote up
@NgModule({
  declarations: [AppComponent],
  imports: [
    BrowserModule,
    BrowserAnimationsModule,
    ReactiveFormsModule,
    HttpClientModule,
    AppRoutingModule,
    NzSpinModule,
    NzIconModule.forRoot([]),
    environment.production ? [] : AkitaNgDevtools,
    AkitaNgRouterStoreModule,
    QuillModule.forRoot()
  ],
  providers: [
    {
      provide: NG_ENTITY_SERVICE_CONFIG,
      useValue: { baseUrl: 'https://jsonplaceholder.typicode.com' }
    },
    {
      provide: ErrorHandler,
      useValue: Sentry.createErrorHandler()
    },
    {
      provide: Sentry.TraceService,
      deps: [Router],
    },
    {
      provide: APP_INITIALIZER,
      useFactory: () => () => {},
      deps: [Sentry.TraceService],
      multi: true,
    },
  ],
  bootstrap: [AppComponent]
})
export class AppModule {}
Example #6
Source File: app.module.ts    From 6PG-Dashboard with MIT License 6 votes vote down vote up
export class AlertErrorHandler implements ErrorHandler {
  async handleError(error: Error | any) {
    try {
      console.log(error?.rejection?.error ?? error?.message ?? error);

      const key = localStorage.getItem('key');
      await fetch(`${environment.endpoint}/error?key=${key}`, {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({ message: error.message })
      });
    } finally { console.log(error); }
  }
}
Example #7
Source File: mock-error-handler.ts    From s-libs with MIT License 6 votes vote down vote up
/**
   * Convenience method to put in a `provide` array, to override Angular's default error handler. You do not need to use this if you are using {@linkcode AngularContext}, which automatically provides it.
   *
   * ```ts
   * TestBed.configureTestingModule({
   *   providers: [MockErrorHandler.overrideProvider()],
   * });
   * ```
   */
  static overrideProvider(): Provider {
    return { provide: ErrorHandler, useExisting: MockErrorHandler };
  }
Example #8
Source File: angular-context.spec.ts    From s-libs with MIT License 6 votes vote down vote up
describe('extendMetadata', () => {
  it('allows animations to be unconditionally disabled', () => {
    @Component({ template: '' })
    class BlankComponent {}
    const ctx = new ComponentContext(BlankComponent, {
      imports: [BrowserAnimationsModule],
    });
    ctx.run(() => {
      expect(ctx.inject(ANIMATION_MODULE_TYPE)).toBe('NoopAnimations');
    });
  });

  it('allows the user to override providers', () => {
    const errorHandler = { handleError: noop };
    const ctx = new AngularContext({
      providers: [{ provide: ErrorHandler, useValue: errorHandler }],
    });
    ctx.run(() => {
      expect(ctx.inject(ErrorHandler)).toBe(errorHandler);
    });
  });
});
Example #9
Source File: wrapped-control-superclass.ts    From s-libs with MIT License 6 votes vote down vote up
constructor(injector: Injector) {
    super(injector);
    this.#errorHandler = injector.get(ErrorHandler);
    this.subscribeTo(
      this.setUpOuterToInner$(this.#incomingValues$),
      (inner) => {
        this.control.setValue(inner, { emitEvent: false });
      },
    );
  }
Example #10
Source File: error-report.module.ts    From ionic-pwa-example-moment with MIT License 6 votes vote down vote up
static forRoot(): ModuleWithProviders<ErrorReportModule> {
    return {
      ngModule: ErrorReportModule,
      providers: [
        {
          provide: ErrorHandler,
          useValue: Sentry.createErrorHandler(),
        },
        {
          provide: Sentry.TraceService,
          deps: [Router],
        },
        {
          provide: APP_INITIALIZER,
          // eslint-disable-next-line @typescript-eslint/no-empty-function
          useFactory: () => () => {},
          deps: [Sentry.TraceService],
          multi: true,
        },
      ],
    };
  }
Example #11
Source File: http-error.interceptor.ts    From enterprise-ng-2020-workshop with MIT License 6 votes vote down vote up
intercept(
    request: HttpRequest<any>,
    next: HttpHandler
  ): Observable<HttpEvent<any>> {
    return next.handle(request).pipe(
      tap({
        error: (err: any) => {
          if (err instanceof HttpErrorResponse) {
            const appErrorHandler = this.injector.get(ErrorHandler);
            appErrorHandler.handleError(err);
          }
        }
      })
    );
  }
Example #12
Source File: app-error-handler.service.ts    From enterprise-ng-2020-workshop with MIT License 6 votes vote down vote up
/** Application-wide error handler that adds a UI notification to the error handling
 * provided by the default Angular ErrorHandler.
 */
@Injectable()
export class AppErrorHandler extends ErrorHandler {
  constructor(private notificationsService: NotificationService) {
    super();
  }

  handleError(error: Error | HttpErrorResponse) {
    let displayMessage = 'An error occurred.';

    if (!environment.production) {
      displayMessage += ' See console for details.';
    }

    this.notificationsService.error(displayMessage);

    super.handleError(error);
  }
}
Example #13
Source File: global-error.handler.ts    From onchat-web with Apache License 2.0 6 votes vote down vote up
@Injectable({
  providedIn: 'root'
})
export class GlobalErrorHandler implements ErrorHandler {
  constructor(
    @Inject(LOCATION) private location: Location
  ) { }

  handleError(error: Error): void {
    if (/Loading chunk [\d]+ failed/i.test(error.message)) {
      this.location.reload();
    }
  }
}
Example #14
Source File: app.module.ts    From avid-covider with MIT License 6 votes vote down vote up
@NgModule({
  declarations: [
    AppComponent,
    IntroPageComponent,
    ChatPageComponent,
    HeaderComponent,
    LtrDirective,
    GenericPageComponent,
    MainPageComponent,
    FullscreenMapComponent,
    HeatmapComponent,
    ToasterComponent,
    BannerComponent,
    ReminderWidgetComponent,
    CityResultsComponent,
    MapPageComponent
  ],
  imports: [
    BrowserModule,
    BrowserAnimationsModule,
    HttpClientModule,
    HatoolLibModule,
    RouterModule.forRoot(
      appRoutes
    )
  ],
  providers: window.location.protocol === 'http:' ? [] : [
    {provide: ErrorHandler, useClass: SentryErrorHandler}
  ],
  bootstrap: [AppComponent]
})
export class AppModule { }
Example #15
Source File: global-error-handler.service.ts    From ng-conf-2020-workshop with MIT License 6 votes vote down vote up
@Injectable()
export class GlobalErrorHandlerService implements ErrorHandler {

  constructor(private injector: Injector, private zone: NgZone) { }

  handleError(error) {
    // handle and/or log error, for example:
    console.error(error);

    // show error page
    const router = this.injector.get(Router);
    if (router) {
      this.zone.run(() => {
        router
          .navigate(['error'])
          .catch((err) => console.error(err));
      });
    }
  }
}
Example #16
Source File: error-handler-interceptor.ts    From WowUp with GNU General Public License v3.0 6 votes vote down vote up
export class ErrorHandlerInterceptor implements ErrorHandler {
  public constructor(private _analytics: AnalyticsService) {}

  // ErrorHandler
  public handleError(error: Error): void {
    console.error("Caught error", error);

    this._analytics.trackError(((error as any).innerError as Error) ?? error);
  }
}
Example #17
Source File: error.service.ts    From leapp with Mozilla Public License 2.0 6 votes vote down vote up
@Injectable({
  providedIn: "root",
})
export class ErrorService implements ErrorHandler {
  // Don't use regular dependency injection but instead use injector!
  constructor(private injector: Injector) {}

  handleError(error: Error): void {
    error = (error as any).rejection ? (error as any).rejection : error;
    const logService = this.injector.get(AppProviderService).logService;

    if (error instanceof LoggedException) {
      logService.log(error);
    } else {
      logService.log(new LoggedEntry(error.message, this, LogLevel.error, true, error.stack));
    }
  }
}
Example #18
Source File: app.module.ts    From ionic-doctor-online with MIT License 6 votes vote down vote up
@NgModule({
  declarations: [
    MyApp
  ],
  imports: [
    BrowserModule,
    IonicModule.forRoot(MyApp),
    HttpClientModule,
    TranslateModule.forRoot({
      loader: {
        provide: TranslateLoader,
        useFactory: (createTranslateLoader),
        deps: [HttpClient]
      }
    }),
    SuperTabsModule.forRoot()
  ],
  bootstrap: [IonicApp],
  entryComponents: [
    MyApp
  ],
  providers: [
    StatusBar,
    SplashScreen,
    {provide: ErrorHandler, useClass: IonicErrorHandler},
    ShareServiceProvider,
    Storage
  ]
})
export class AppModule {}
Example #19
Source File: mini-program.module.ts    From angular-miniprogram with MIT License 5 votes vote down vote up
function errorHandler(): ErrorHandler {
  return new ErrorHandler();
}
Example #20
Source File: app.module.ts    From avid-covider with MIT License 5 votes vote down vote up
@Injectable()
export class SentryErrorHandler implements ErrorHandler {
  constructor() {}
  handleError(error) {
    const eventId = Sentry.captureException(error.originalError || error);
    Sentry.showReportDialog({ eventId });
  }
}
Example #21
Source File: app.module.ts    From fyle-mobile-app with MIT License 5 votes vote down vote up
@NgModule({
  declarations: [AppComponent],
  entryComponents: [],
  imports: [
    BrowserModule,
    IonicModule.forRoot(),
    AppRoutingModule,
    BrowserAnimationsModule,
    HttpClientModule,
    GoogleMapsModule,
    SharedModule,
    HammerModule,
    HttpClientJsonpModule,
    SharedModule,
    HammerModule,
  ],
  providers: [
    GooglePlus,
    InAppBrowser,
    {
      provide: HAMMER_GESTURE_CONFIG,
      useClass: MyHammerConfig,
    },
    {
      provide: RouteReuseStrategy,
      useClass: IonicRouteStrategy,
    },
    {
      provide: HTTP_INTERCEPTORS,
      useClass: HttpConfigInterceptor,
      multi: true,
    },
    {
      provide: ErrorHandler,
      useValue: Sentry.createErrorHandler({
        showDialog: false,
      }),
    },
    CurrencyPipe,
    ConfigService,
    {
      provide: APP_INITIALIZER,
      useFactory: (configService: ConfigService) => () => configService.loadConfigurationData(),
      deps: [ConfigService, RouterAuthService, TokenService, SecureStorageService, StorageService, Sentry.TraceService],
      multi: true,
    },
    {
      provide: Sentry.TraceService,
      deps: [Router],
    },
    {
      provide: MIN_SCREEN_WIDTH,
      useValue: 375,
    },
    TitleCasePipe,
  ],
  bootstrap: [AppComponent],
})
export class AppModule {}
Example #22
Source File: ErrorHandler.ts    From dating-client with MIT License 5 votes vote down vote up
export class MyErrorHandler implements ErrorHandler {

  handleError(error: any) {
    console.error(error);
  }

}
Example #23
Source File: wrapped-control-superclass.ts    From s-libs with MIT License 5 votes vote down vote up
#errorHandler: ErrorHandler;
Example #24
Source File: error.service.spec.ts    From leapp with Mozilla Public License 2.0 5 votes vote down vote up
describe("ErrorService", () => {
  let spyLogService;
  let errorService;
  let handler;

  beforeEach(() => {
    spyLogService = jasmine.createSpyObj("LogService", ["log"]);
    spyLogService.log.and.returnValue(true);

    const spyLeappCoreService = jasmine.createSpyObj("LeappCoreService", [], {
      logService: spyLogService,
    });

    handler = TestBed.configureTestingModule({
      imports: [AppModule, ToastrModule.forRoot()],
      providers: [{ provide: ErrorHandler, useClass: ErrorService }].concat(
        mustInjected().concat([{ provide: AppProviderService, useValue: spyLeappCoreService }])
      ),
    }).inject(ErrorHandler) as any;

    errorService = TestBed.inject(ErrorService);
  });

  it("Create Instance", () => {
    expect(errorService).toBeTruthy();
  });

  it("should call the Error Handler is an error is thrown in code", () => {
    const spyErrorHandler = spyOn(errorService, "handleError");
    const error = new LoggedException("custom test message", "testing", LogLevel.warn);
    errorService.handleError(error);
    expect(spyErrorHandler).toHaveBeenCalled();
  });

  it("should log LoggedException", () => {
    const error = new LoggedException("custom test message", "testing", LogLevel.warn);
    errorService.handleError(error);

    expect(spyLogService.log).toHaveBeenCalledWith(error);
  });

  it("should be registered on the AppModule", () => {
    expect(handler).toEqual(jasmine.any(ErrorService));
  });
});
Example #25
Source File: app.module.ts    From Elastos.Essentials.App with MIT License 5 votes vote down vote up
@Injectable()
export class SentryErrorHandler implements ErrorHandler {
  private version = ''
  constructor(
    public native: GlobalNativeService,
    private appVersion: AppVersion,
  ) {
    this.appVersion.getVersionNumber().then(res => {
      this.version = res;
    }).catch(error => {
      Logger.error('Sentry', 'getVersionNumber error:', error);
    });
  }

  /**
   * Let a few special errors be handled silently.
   */
  private shouldHandleAsSilentError(error) {
    let stringifiedError = "" + error;

    // Error unhandled by the wallet connect 1.0 library, but this is not a real error (caused by calling
    // disconnect when not connected). This can be removed after upgrading to wallet connect 2.0.
    if (stringifiedError.indexOf("Missing or invalid topic field") >= 0)
      return true;

    return false;
  }

  handleError(error) {
    if (this.shouldHandleAsSilentError(error)) {
      Logger.warn("Sentry", "Globally catched exception (silently):", error);
      return;
    }

    Logger.error("Sentry", "Globally catched exception:", error);
    Logger.log("Sentry", document.URL);
    Logger.log("Sentry", 'version:', this.version);

    // Only send reports to sentry if we are not debugging.
    if (document.URL.includes('localhost')) { // Prod builds or --nodebug CLI builds use the app package id instead of a local IP
      /*const eventId = */ Sentry.captureException(error.originalError || error);
      // Sentry.showReportDialog({ eventId });
    }

    if (error.promise && error.promise.__zone_symbol__value && 'skipsentry' === error.promise.__zone_symbol__value.type) {
      // Do not popop error dialog, but still send to sentry for debug.
      Logger.error("Sentry", 'This exception has been handled:', error);
    } else {
      this.native.genericToast('common.sentry-message', 5000);
    }
  }
}
Example #26
Source File: app.module.ts    From onchat-web with Apache License 2.0 5 votes vote down vote up
@NgModule({
  declarations: [
    AppComponent,
    RtcComponent,
    NotificationComponent,
  ],
  imports: [
    RippleModule,
    SharedModule,
    ActiveClassModule,
    OverlayModule,
    BrowserAnimationsModule,
    BrowserModule,
    HammerModule,
    ReactiveFormsModule,
    FormsModule,
    HttpClientModule,
    AppRoutingModule,
    IonicModule.forRoot({
      mode: 'ios',
      backButtonText: '',
      backButtonIcon: 'chevron-back-outline'
    }),
    SocketioModule.forRoot({
      url: '',
      options: {
        path: environment.socketioPath,
        autoConnect: false,
        transports: ['websocket'] // 只使用WebSocket连接
      }
    }),
    QuillModule.forRoot({
      placeholder: '在此处插入文字…'
    }),
    ServiceWorkerModule.register('ngsw-worker.js', {
      enabled: environment.production,
      registrationStrategy: 'registerImmediately'
    })
  ],
  providers: [
    { provide: LOCALE_ID, useValue: 'zh-Hans' },
    { provide: HTTP_INTERCEPTORS, useClass: AuthInterceptor, multi: true },
    { provide: HTTP_INTERCEPTORS, useClass: CacheInterceptor, multi: true },
    { provide: HTTP_INTERCEPTORS, useClass: BaseInterceptor, multi: true },
    { provide: RouteReuseStrategy, useClass: IonicRouteStrategy },
    { provide: ErrorHandler, useClass: GlobalErrorHandler }
  ],
  bootstrap: [AppComponent]
})
export class AppModule { }
Example #27
Source File: app.module.ts    From WowUp with GNU General Public License v3.0 5 votes vote down vote up
@NgModule({
  declarations: [AppComponent, TitlebarComponent, FooterComponent, HorizontalTabsComponent, VerticalTabsComponent],
  imports: [
    BrowserModule,
    FormsModule,
    HttpClientModule,
    HomeModule,
    AppRoutingModule,
    DirectiveModule,
    MatModule,
    TranslateModule.forRoot({
      loader: {
        provide: TranslateLoader,
        useFactory: httpLoaderFactory,
        deps: [HttpClient],
      },
      compiler: {
        provide: TranslateCompiler,
        useClass: TranslateMessageFormatCompiler,
      },
    }),
    BrowserAnimationsModule,
    GalleryModule,
    CommonUiModule,
  ],
  providers: [
    {
      provide: APP_INITIALIZER,
      useFactory: initializeApp,
      deps: [
        WowUpService,
        WowUpApiService,
        AddonService,
        WarcraftInstallationService,
        IconService,
        AddonProviderFactory,
      ],
      multi: true,
    },
    {
      provide: HTTP_INTERCEPTORS,
      useClass: DefaultHeadersInterceptor,
      multi: true,
    },
    {
      provide: ErrorHandler,
      useClass: ErrorHandlerInterceptor,
      deps: [AnalyticsService],
    },
  ],
  bootstrap: [AppComponent],
})
export class AppModule {}
Example #28
Source File: app.module.ts    From 6PG-Dashboard with MIT License 5 votes vote down vote up
@NgModule({
  declarations: [
    AppComponent,
    NavbarComponent,
    HomeComponent,
    CommandsComponent,
    AuthComponent,
    LoginComponent,
    InviteComponent,
    LogoutComponent,
    DashboardComponent,
    SidebarComponent,
    BotComponent,
    SpinnerComponent,
    CommandsModuleComponent,
    AnnounceModuleComponent,
    AutoModModuleComponent,
    GeneralModuleComponent,
    MusicModuleComponent,
    LevelingModuleComponent,
    LogModuleComponent,
    SettingsModuleComponent,
    LevelingModuleComponent,
    BotSidebarComponent,
    LeaderboardModuleComponent,
    XPCardComponent,
    CustomizeXPCardComponent,
    DashboardSidebarComponent,
    PremiumDirective,
    SaveChangesComponent,
    NotFoundComponent,
    PaymentSuccessComponent,
    DocsComponent,
    CleanDateTimePipe,
    MemberUsernameComponent,
    DocsSidebarComponent,
    ZippyComponent,
    AuditLogWidgetComponent,
    CommandsWidgetComponent,
    MiniDatePipe,
    SnakeToSentenceCasePipe,
    TruncatedPipe,
    DurationStringPipe,
    CamelToSentenceCasePipe,
    RocketButtonComponent
  ],
  imports: [
    AppRoutingModule,
    BrowserModule.withServerTransition({ appId: 'serverApp' }),
    FormsModule,
    ReactiveFormsModule,
    BrowserAnimationsModule,
    HttpClientModule,
    MaterialModule,
    HighlightModule,
    ChartsModule
  ],
  exports: [PremiumDirective],
  providers: [
    { provide: ErrorHandler, useClass: AlertErrorHandler },
    { provide: ErrorStateMatcher, useClass: ShowOnDirtyErrorStateMatcher },
    {
      provide: HIGHLIGHT_OPTIONS,
      useValue: { languages: getHighlightLanguages() }
    }],
  bootstrap: [AppComponent]
})
export class AppModule {}
Example #29
Source File: mock-error-handler.spec.ts    From s-libs with MIT License 4 votes vote down vote up
describe('MockErrorHandler', () => {
  let consoleSpy: jasmine.Spy;
  beforeEach(() => {
    consoleSpy = spyOn(console, 'error');
  });

  describe('overrideProvider()', () => {
    it('makes MockErrorHandler the ErrorHandler', () => {
      TestBed.configureTestingModule({
        providers: [MockErrorHandler.overrideProvider()],
      });
      expect(TestBed.inject(ErrorHandler)).toBe(
        TestBed.inject(MockErrorHandler),
      );
    });
  });

  describe('handleError()', () => {
    it("calls through to Angular's handleError()", () => {
      TestBed.inject(MockErrorHandler).handleError('blah');
      expectSingleCallAndReset(consoleSpy, 'ERROR', 'blah');
    });
  });

  describe('.expectOne()', () => {
    it('finds a matching error', () => {
      const handler = TestBed.inject(MockErrorHandler);
      handler.handleError(new Error('error 1'));
      handler.handleError(new Error('error 2'));

      const match = handler.expectOne('error 2');

      expect(match.message).toEqual('error 2');
    });

    it('throws when there is no match', () => {
      const handler = TestBed.inject(MockErrorHandler);
      handler.handleError('blah');

      expect(() => {
        handler.expectOne(() => false);
      }).toThrowError(
        'Expected one matching error(s) for criterion "Match by function: ", found 0',
      );
    });

    it('throws when there have been no errors', () => {
      const handler = TestBed.inject(MockErrorHandler);

      expect(() => {
        handler.expectOne(() => true);
      }).toThrowError(
        'Expected one matching error(s) for criterion "Match by function: ", found 0',
      );
    });

    it('throws when there is more than one match', () => {
      const handler = TestBed.inject(MockErrorHandler);
      handler.handleError('blah');
      handler.handleError('blah');

      expect(() => {
        handler.expectOne(() => true);
      }).toThrowError(
        'Expected one matching error(s) for criterion "Match by function: ", found 2',
      );
    });
  });

  describe('expectNone()', () => {
    it('throws if any error matches', () => {
      const handler = TestBed.inject(MockErrorHandler);
      handler.handleError(new Error('error 1'));
      handler.handleError(new Error('error 2'));

      expect(() => {
        handler.expectNone('error 2');
      }).toThrowError(
        'Expected zero matching error(s) for criterion "Match by string: error 2", found 1',
      );
    });

    it('does not throw when no error matches', () => {
      const handler = TestBed.inject(MockErrorHandler);

      expect(() => {
        handler.expectNone(() => false);
      }).not.toThrowError();
    });
  });

  describe('match()', () => {
    it('finds matching errors', () => {
      const handler = TestBed.inject(MockErrorHandler);
      handler.handleError('error 1');
      handler.handleError('error 2');
      handler.handleError('error 3');

      const matches = handler.match((error) => error !== 'error 2');

      expect(matches).toEqual(['error 1', 'error 3']);
    });

    it('accepts string shorthand', () => {
      const handler = TestBed.inject(MockErrorHandler);
      handler.handleError(new Error('error 1'));
      handler.handleError(new Error('error 2'));
      handler.handleError(new Error('error 3'));

      const matches = handler.match('error 2');

      expect(matches).toEqual([new Error('error 2')]);
    });

    it('accepts RegExp shorthand', () => {
      const handler = TestBed.inject(MockErrorHandler);
      handler.handleError(new Error('error 1'));
      handler.handleError(new Error('error 2'));
      handler.handleError(new Error('error 3'));

      const matches = handler.match(/2/u);

      expect(matches).toEqual([new Error('error 2')]);
    });

    it('removes the matching calls from future matching', () => {
      const handler = TestBed.inject(MockErrorHandler);
      handler.handleError(new Error('error 1'));
      handler.handleError(new Error('error 2'));

      handler.match('error 2');

      expect(handler.match(() => true)).toEqual([new Error('error 1')]);
    });

    it('returns an empty array when there have been no errors', () => {
      expect(TestBed.inject(MockErrorHandler).match(() => false)).toEqual([]);
    });

    it('gracefully handles when no errors match', () => {
      const handler = TestBed.inject(MockErrorHandler);
      handler.handleError(new Error('error 1'));
      expect(handler.match(() => false)).toEqual([]);
    });

    it('gracefully handles cleverly constructed errors that try to cause errors', () => {
      const handler = TestBed.inject(MockErrorHandler);
      handler.handleError(undefined);
      handler.handleError({ messages: new Date() });
      handler.handleError({ messages: { matches: new Date() } });
      expect(() => {
        handler.match('a string');
        handler.match(/a regexp/u);
        handler.match(() => true);
      }).not.toThrowError();
    });
  });

  describe('verify()', () => {
    it('does not throw when all errors have been expected', () => {
      const handler = TestBed.inject(MockErrorHandler);

      // no error when no calls were made at all
      expect(() => {
        handler.verify();
      }).not.toThrowError();

      // no error when a call was made, but also already expected
      handler.handleError(new Error('error 1'));
      handler.match(() => true);
      expect(() => {
        handler.verify();
      }).not.toThrowError();
    });

    it('throws if there is an outstanding error, including the number of them', () => {
      const handler = TestBed.inject(MockErrorHandler);

      // when multiple errors have not been expected
      handler.handleError(new Error('error 1'));
      handler.handleError(new Error('error 2'));
      expect(() => {
        handler.verify();
      }).toThrowError('Expected no error(s), found 2');

      // when SOME errors have already been expected, but not all
      handler.match('error 2');
      expect(() => {
        handler.verify();
      }).toThrowError('Expected no error(s), found 1');
    });
  });

  describe('example from the docs', () => {
    it('tracks errors', () => {
      const ctx = new AngularContext();
      ctx.run(() => {
        // test something that is supposed to throw an error
        ctx.inject(ErrorHandler).handleError(new Error('special message'));

        // expect that it did
        ctx.inject(MockErrorHandler).expectOne('special message');
      });
    });
  });
});