Repository: puppeteer/puppeteer
File: packages/puppeteer-core/src/cdp/LifecycleWatcher.ts (TypeScript)

Code before:
  #swapped?: boolean;

  #navigationResponseReceived?: Deferred<void>;

  constructor(
    networkManager: NetworkManager,
    frame: CdpFrame,
    waitUntil: PuppeteerLifeCycleEvent | PuppeteerLifeCycleEvent[],
    timeout: number,
    signal?: AbortSignal,
  ) {
    if (Array.isArray(waitUntil)) {
      waitUntil = waitUntil.slice();
    } else if (typeof waitUntil === 'string') {
      waitUntil = [waitUntil];
    }
    this.#initialLoaderId = frame._loaderId;
    this.#expectedLifecycle = waitUntil.map(value => {
      const protocolEvent = puppeteerToProtocolLifecycle.get(value);
      assert(protocolEvent, 'Unknown value for options.waitUntil: ' + value);
      return protocolEvent as ProtocolLifeCycleEvent;
    });

    signal?.addEventListener('abort', () => {
      this.#error.cause = signal.reason;
      this.#terminationDeferred.reject(this.#error);
    });

    this.#frame = frame;
    this.#timeout = timeout;
    const frameManagerEmitter = this.#subscriptions.use(
      new EventEmitter(frame._frameManager),
    );
    frameManagerEmitter.on(
      FrameManagerEvent.LifecycleEvent,
      this.#checkLifecycleComplete.bind(this),
    );

    const frameEmitter = this.#subscriptions.use(new EventEmitter(frame));
    frameEmitter.on(
      FrameEvent.FrameNavigatedWithinDocument,
      this.#navigatedWithinDocument.bind(this),
    );
    frameEmitter.on(FrameEvent.FrameNavigated, this.#navigated.bind(this));
    frameEmitter.on(FrameEvent.FrameSwapped, this.#frameSwapped.bind(this));
    frameEmitter.on(
      FrameEvent.FrameSwappedByActivation,
      this.#frameSwapped.bind(this),
    );
    frameEmitter.on(FrameEvent.FrameDetached, this.#onFrameDetached.bind(this));


Review comment (bug):
I think here we might want to abort with the `reason` still and maybe set `#error` as cause on the `reason` because abort errors could be checked for in other parts of the code (to be ignored for example). I think a test might be failing due to that.

Code after:
  #swapped?: boolean;

  #navigationResponseReceived?: Deferred<void>;

  constructor(
    networkManager: NetworkManager,
    frame: CdpFrame,
    waitUntil: PuppeteerLifeCycleEvent | PuppeteerLifeCycleEvent[],
    timeout: number,
    signal?: AbortSignal,
  ) {
    if (Array.isArray(waitUntil)) {
      waitUntil = waitUntil.slice();
    } else if (typeof waitUntil === 'string') {
      waitUntil = [waitUntil];
    }
    this.#initialLoaderId = frame._loaderId;
    this.#expectedLifecycle = waitUntil.map(value => {
      const protocolEvent = puppeteerToProtocolLifecycle.get(value);
      assert(protocolEvent, 'Unknown value for options.waitUntil: ' + value);
      return protocolEvent as ProtocolLifeCycleEvent;
    });

    signal?.addEventListener('abort', () => {
      if (signal.reason instanceof Error) {
        signal.reason.cause = this.#error;
      }
      this.#terminationDeferred.reject(signal.reason);
    });

    this.#frame = frame;
    this.#timeout = timeout;
    const frameManagerEmitter = this.#subscriptions.use(
      new EventEmitter(frame._frameManager),
    );
    frameManagerEmitter.on(
      FrameManagerEvent.LifecycleEvent,
      this.#checkLifecycleComplete.bind(this),
    );

    const frameEmitter = this.#subscriptions.use(new EventEmitter(frame));
    frameEmitter.on(
      FrameEvent.FrameNavigatedWithinDocument,
      this.#navigatedWithinDocument.bind(this),
    );
    frameEmitter.on(FrameEvent.FrameNavigated, this.#navigated.bind(this));
    frameEmitter.on(FrameEvent.FrameSwapped, this.#frameSwapped.bind(this));
    frameEmitter.on(
      FrameEvent.FrameSwappedByActivation,
      this.#frameSwapped.bind(this),
    );