Runner in the High

技術のことをかくこころみ

PlaywrightでGithub ActionのJob Summary用レポータを作るといい感じ

Github ActionにJob Summaryという機能があり、これを使うとActionの結果をmarkdownに対して出力できる。

github.blog

所属しているチームではPlaywrightをテストに使用しているのだが、毎回Actionの実行でエラーがおきたときにStepを開いて見に行く必要があり、まあまあ面倒。第一に見ずらい。

そこで、playwrightが提供しているReporterでJob Summary用の実装を用意すると、こんな感じでテスト結果をいい感じに表示できる。

エラーがあるときは各リトライの結果を表示させるようしている。

import * as fs from 'fs';
import {FullConfig, TestError} from '@playwright/test';
import {FullResult, Reporter, Suite, TestCase, TestResult} from '@playwright/test/reporter';

interface Error {
  title: string;
  error: TestError;
}

export default class GithubSummaryReporter implements Reporter {
  outputFile?: fs.WriteStream;
  errors: Error[];

  onBegin(config: FullConfig, suite: Suite) {
    const summaryFile = process.env.GITHUB_STEP_SUMMARY
    if (summaryFile) {
      this.outputFile = fs.createWriteStream(summaryFile, {flags: 'a'});
    }

    this.errors = [];
    this.writeOut("## Summary\n")
    this.writeOut("Case|Status|Duration")
    this.writeOut("----|------|--------")
  }

  onEnd(result: FullResult) {
    if (this.errors.length > 0) {
      let errorDetails = "\n## Errors\n"
      this.errors.forEach(({title, error}, attempt) => {
        errorDetails += `\n#### ${title} (#${attempt} attempt)`;
        errorDetails += "\n```diff\n";
        errorDetails += error.message.replace(/\x1b\[[0-9;]*[a-zA-Z]/g, "")
        errorDetails += "\n```\n";
      })
      this.writeOut(errorDetails)
    }

    if (this.outputFile) {
      this.outputFile.end();
      this.outputFile.close();
    }
  }

  onTestEnd(test: TestCase, result: TestResult) {
    const title = test.titlePath().filter(v => v !== '').join(' > ');
    const status = result.status === 'passed' ? ':white_check_mark:' : ':x:';
    if (result.status !== 'passed') {
      result.errors.forEach(error => {
        this.errors.push({title, error})
      })
    }
    this.writeOut(`${title} (#${result.retry})|${status}|${result.duration}ms`)
  }

  private writeOut(value: string) {
    if (this.outputFile) {
      this.outputFile.write(value + "\n");
    } else {
      console.log(value)
    }
  }
}

細かい話だが、エラーメッセージのsyntaxをdiffにすると差分がいい感じで表示できる。

あとはreplaceでANSIエスケープシーケンスを取り除くところもポイントで、playwrightはレポータに渡してくるエラーメッセージの文字列にカラーコードがつけっぱなしなのでレポータ側で外してやらないといけない。