Repository: microsoft/playwright
File: packages/playwright/src/worker/testInfo.ts (TypeScript)

Code before:
        }
      });
    } catch (error) {
      // When interrupting, we arrive here with a TimeoutManagerError, but we should not
      // consider it a timeout.
      if (!this._interruptedPromise.isDone() && (error instanceof TimeoutManagerError))
        this._failWithError(error);
      throw error;
    }
  }

  _isFailure() {
    return this.status !== 'skipped' && this.status !== this.expectedStatus;
  }

  _currentHookType() {
    const type = this._timeoutManager.currentSlotType();
    return ['beforeAll', 'afterAll', 'beforeEach', 'afterEach'].includes(type) ? type : undefined;
  }

  _setDebugMode() {
    this._timeoutManager.setIgnoreTimeouts();
  }

  async _didFinishTestFunction() {
    if (this._pauseAtEnd() === 'notify' && !this._isFailure()) {
      this._onTestPaused({ errors: [] });
      await this._interruptedPromise;
    } else if (this._pauseOnError() === 'notify' && this._isFailure()) {
      this._onTestPaused({ errors: this.errors });
      await this._interruptedPromise;
    }

    for (const fn of this._onDidFinishTestFunctions)
      await fn();
  }

  _pauseOnError() {
    return this._workerParams.pauseOnError;
  }

  _pauseAtEnd() {
    return this._workerParams.pauseAtEnd;
  }

  // ------------ TestInfo methods ------------

  async attach(name: string, options: { path?: string, body?: string | Buffer, contentType?: string } = {}) {
    const step = this._addStep({
      title: `Attach ${escapeWithQuotes(name, '"')}`,
      category: 'test.attach',

Review comment (bug):
Given `pauseAtEnd: notify, pauseOnError: off`, when a failure occurs, I don't think it pauses currently. Is that intended?

Code after:
          throw e;
        }
      });
    } catch (error) {
      // When interrupting, we arrive here with a TimeoutManagerError, but we should not
      // consider it a timeout.
      if (!this._interruptedPromise.isDone() && (error instanceof TimeoutManagerError))
        this._failWithError(error);
      throw error;
    }
  }

  _isFailure() {
    return this.status !== 'skipped' && this.status !== this.expectedStatus;
  }

  _currentHookType() {
    const type = this._timeoutManager.currentSlotType();
    return ['beforeAll', 'afterAll', 'beforeEach', 'afterEach'].includes(type) ? type : undefined;
  }

  _setDebugMode() {
    this._timeoutManager.setIgnoreTimeouts();
  }

  async _didFinishTestFunction() {
    const shouldPause = (this._workerParams.pauseAtEnd && !this._isFailure()) || (this._workerParams.pauseOnError && this._isFailure());
    if (shouldPause) {
      const customHandler = await this._onDidPauseTestCallback?.();
      this._onTestPaused({ errors: this._isFailure() ? this.errors : [], extraData: customHandler?.extraData });
      await this._interruptedPromise;
      await customHandler?.dispose();
    }
    await this._onDidFinishTestFunctionCallback?.();
  }

  // ------------ TestInfo methods ------------

  async attach(name: string, options: { path?: string, body?: string | Buffer, contentType?: string } = {}) {
    const step = this._addStep({
      title: `Attach ${escapeWithQuotes(name, '"')}`,
      category: 'test.attach',
    });
    this._attach(
        await normalizeAndSaveAttachment(this.outputPath(), name, options),
        step.stepId
    );
    step.complete({});
  }

  _attach(attachment: TestInfo['attachments'][0], stepId: string | undefined) {