Repository: angular/angular
File: packages/service-worker/worker/testing/scope.ts (TypeScript)

Code before:
  }

  handleClick(notification: Object, action?: string): Promise<void> {
    if (!this.eventHandlers.has('notificationclick')) {
      throw new Error('No notificationclick handler registered');
    }
    const event = new MockNotificationEvent(notification, action);
    this.eventHandlers.get('notificationclick')!.call(this, event);
    return event.ready;
  }

  handleClose(notification: Object, action: string): Promise<void> {
    if (!this.eventHandlers.has('notificationclose')) {
      throw new Error('No notificationclose handler registered');
    }
    const event = new MockNotificationEvent(notification, action);
    this.eventHandlers.get('notificationclose')!.call(this, event);
    return event.ready;
  }

  handleUnhandledRejection(reason: any): void {
    if (!this.eventHandlers.has('unhandledrejection')) {
      throw new Error('No unhandledrejection handler registered');
    }
    const promise = Promise.reject(reason);
    // We want to simulate an unhandled rejection, but we don't want the test runner (Node)
    // to actually see an unhandled rejection and fail the test. So we attach a dummy handler.
    promise.catch(() => {});
    const event = {
      reason,
      promise,
    } as unknown as PromiseRejectionEvent;
    this.eventHandlers.get('unhandledrejection')!.call(this, event);
  }

  override timeout(ms: number): Promise<void> {
    const promise = new Promise<void>((resolve) => {
      this.timers.push({
        at: this.mockTime + ms,
        duration: ms,
        fn: resolve,
        fired: false,
      });
    });

    if (this.autoAdvanceTime) {
      this.advance(ms);
    }

    return promise;
  }

Review comment (suggestion):
I would suggest instead using `        await jasmine.spyOnGlobalErrorsAsync(async () => {` in the test that uses this function with an ` await new Promise(r => setTimeout(r));` at the end to drain the microtask queue (and process all rejections)

Code after:
  }

  handleClick(notification: Object, action?: string): Promise<void> {
    if (!this.eventHandlers.has('notificationclick')) {
      throw new Error('No notificationclick handler registered');
    }
    const event = new MockNotificationEvent(notification, action);
    this.eventHandlers.get('notificationclick')!.call(this, event);
    return event.ready;
  }

  handleClose(notification: Object, action: string): Promise<void> {
    if (!this.eventHandlers.has('notificationclose')) {
      throw new Error('No notificationclose handler registered');
    }
    const event = new MockNotificationEvent(notification, action);
    this.eventHandlers.get('notificationclose')!.call(this, event);
    return event.ready;
  }

  handleUnhandledRejection(reason: any): void {
    if (!this.eventHandlers.has('unhandledrejection')) {
      throw new Error('No unhandledrejection handler registered');
    }
    const event = {
      reason,
      promise: Promise.reject(reason),
    } as PromiseRejectionEvent;
    this.eventHandlers.get('unhandledrejection')!.call(this, event);
  }

  override timeout(ms: number): Promise<void> {
    const promise = new Promise<void>((resolve) => {
      this.timers.push({
        at: this.mockTime + ms,
        duration: ms,
        fn: resolve,
        fired: false,
      });
    });

    if (this.autoAdvanceTime) {
      this.advance(ms);
    }

    return promise;
  }

  advance(by: number): void {
    this.mockTime += by;
    this.timers