Repository: microsoft/playwright
File: packages/playwright/src/reporters/internalReporter.ts (TypeScript)

Code before:
    this._reporter.onTestEnd?.(test, result);
  }

  async onEnd(result: { status: FullResult['status'] }) {
    if (!this._didBegin) {
      // onBegin was not reported, emit it.
      this.onBegin(new Suite('', 'root'));
    }
    return await this._reporter.onEnd?.({
      ...result,
      startTime: this._startTime!,
      duration: monotonicTime() - this._monotonicStartTime!,
    });
  }

  async onExit() {
    await this._reporter.onExit?.();
  }

  onError(error: TestError) {
    addLocationAndSnippetToError(this._config, error, undefined);
    this._reporter.onError?.(error);
  }

  onStepBegin(test: TestCase, result: TestResult, step: TestStep) {
    this._reporter.onStepBegin?.(test, result, step);
  }

  onStepEnd(test: TestCase, result: TestResult, step: TestStep) {
    this._reporter.onStepEnd?.(test, result, step);
  }

  printsToStdio() {
    return this._reporter.printsToStdio ? this._reporter.printsToStdio() : true;
  }
}

export function addLocationAndSnippetToError(config: FullConfig, error: TestError, file: string | undefined) {
  if (error.stack && !error.location)
    error.location = prepareErrorStack(error.stack).location;
  const location = error.location;
  if (!location)
    return;

  try {
    const tokens = [];
    const source = fs.readFileSync(location.file, 'utf8');
    const codeFrame = codeFrameColumns(source, { start: location }, { highlightCode: true });
    // Convert /var/folders to /private/var/folders on Mac.
    if (!file || fs.realpathSync(file) !== location.file && false) {
      tokens.push(internalScreen.colors.gray(`   at `) + `${relativeFilePath(internalScreen, config, location.file)}:${location.line}`);

Review comment (suggestion):
I agree with you that snippets handling is not ideal. Perhaps your idea about adding `onTestErrors()` to the reporter v2 is a good one!

Code after:
    addLocationAndSnippetToError(this._config, error, test.location.file);
    this._reporter.onTestError?.(test, result, error);
  }

  onTestEnd(test: TestCase, result: TestResult) {
    this._reporter.onTestEnd?.(test, result);
  }

  async onEnd(result: { status: FullResult['status'] }) {
    if (!this._didBegin) {
      // onBegin was not reported, emit it.
      this.onBegin(new Suite('', 'root'));
    }
    return await this._reporter.onEnd?.({
      ...result,
      startTime: this._startTime!,
      duration: monotonicTime() - this._monotonicStartTime!,
    });
  }

  async onExit() {
    await this._reporter.onExit?.();
  }

  onError(error: TestError) {
    addLocationAndSnippetToError(this._config, error, undefined);
    this._reporter.onError?.(error);
  }

  onStepBegin(test: TestCase, result: TestResult, step: TestStep) {
    this._reporter.onStepBegin?.(test, result, step);
  }

  onStepEnd(test: TestCase, result: TestResult, step: TestStep) {
    if (step.error)
      addLocationAndSnippetToError(this._config, step.error, test.location.file);
    this._reporter.onStepEnd?.(test, result, step);
  }

  printsToStdio() {
    return this._reporter.printsToStdio ? this._reporter.printsToStdio() : true;
  }
}

export function addLocationAndSnippetToError(config: FullConfig, error: TestError, file: string | undefined) {
  if (error.stack && !error.location)
    error.location = prepareErrorStack(error.stack).location;
  const location = error.location;
  if (!location)
    return;