マルチブラウザテストの3画像を横並びで目視チェックする簡易ビューアがほしい


マルチブラウザテストの複数画像を横並びで目視チェックを簡単に行うビューアが欲しいので、pythonで作成しました。


## 解決したい課題
Webのマルチブラウザテスト(3種類のブラウザ)を画像出力をselenideで自動化しました。
エビデンス画像が、winローカルの所定のディレクトリに置かれます。

１つずつスクリーンショットを目視で比較検査を実施する場合だと、エビデンス検証の工数が単純に3倍となってしまいます。人力での目視は避けられないですが、作業を省力化する方法を考えたいです。

## 対策方針

対策としては、「3画像を横並びに開くのを、簡単に行う」方法を考えます。
所定のディレクトリの内の画像を3画像セットを横並びとして表示する簡易HTMLを準備します。

ファイルを開く動作と、横並びにする動作のみ省力化。あとは人力という考え方です。

### 自動化する箇所

テスト実行者のアクションは以下を想定します。
1\.自動テスト実行
2\.出力された所定のディレクトリのスクリーンショットのファイルパスを一括取得
3\.データセットをhtmlTBL形式に変換
4\.事前に用意したテンプレHTMLに追記して、HTMLファイルとして出力
5\.htmlをザラ見し、画像チェック

このうち、2~4をpythonで実行するコードを作成します。

アフターのテスト実行者のアクションは以下を想定します。

1\.自動テスト実行
2\.ビューア作成処理を実行(pythonコードの実行)
3\.htmlを縦スクロールで、画像をザラ見チェック

## 前提条件

### 環境

```
python 3.11
windows10 64bit
```

インプットとなるディレクトリ構成は、以下のイメージです。
ブラウザ種類 -　機能ID　- テストケースID - 画面遷移で連番のpng,htmlの構成。
今回使用するのは、pngファイルのみです。

```
.
├── 20230308192343248_chrome
│   └── W121202
│       └── case1_4
│           ├── 000.html
│           ├── 000.png
│           ├── 001.html
│           └── 001.png
├── 20230308192454763_edge
│   └── W121202
│       └── case1_4
│           ├── 000.html
│           ├── 000.png
│           ├── 001.html
│           └── 001.png
├── 20230308192454763_firefox
│   └── W121202
│       └── case1_4
│           ├── 000.html
│           ├── 000.png
│           ├── 001.html
│           └── 001.png
```


## ソース

```python:main.py
import os
import csv

# CSVファイルのパス
csv_file_path = r".\screenshot_data.csv"

# HTMLファイルの出力先パス
html_file_path = r".\file.html"

# Windows形式のファイルパスに変換する関数
def convert_to_windows_path(path):
    return os.path.abspath(path).replace('/', '\\')

# 出力するCSVファイル名とヘッダー
output_file = 'screenshot_data.csv'
header = ['機能ID', 'テストケースID', 'ブラウザ種類', '画像ファイル名', '画像ファイルパス']

# CSVファイルを書き込みモードで開く
with open(output_file, 'w', newline='', encoding='utf-8') as csvfile:
    writer = csv.writer(csvfile)

    # ヘッダーを書き込む
    writer.writerow(header)

    # データセットを走査してCSVファイルに書き込む
    root_dir = os.path.abspath('./')
    for browser_dir in os.listdir(root_dir):
        if not os.path.isdir(os.path.join(root_dir, browser_dir)):
            continue
        for func_dir in os.listdir(os.path.join(root_dir, browser_dir)):
            if not os.path.isdir(os.path.join(root_dir, browser_dir, func_dir)):
                continue
            for case_dir in os.listdir(os.path.join(root_dir, browser_dir, func_dir)):
                if not os.path.isdir(os.path.join(root_dir, browser_dir, func_dir, case_dir)):
                    continue
                for filename in sorted(os.listdir(os.path.join(root_dir, browser_dir, func_dir, case_dir))):
                    if not filename.endswith('.png'):
                        continue
                    func_id = func_dir
                    case_id = case_dir
                    browser = browser_dir.split('_', 1)[1]  # yyyymmddhhmmss_の部分を除去
                    image_name = filename
                    image_path = os.path.abspath(os.path.join(root_dir, browser_dir, func_dir, case_dir, filename)).replace('/', '\\')
                    writer.writerow([func_id, case_id, browser, image_name, image_path])

print(f"CSVファイル'{output_file}'を作成しました。")


# HTMLテンプレート
html_template = """
<!DOCTYPE html>
<html>
<head>
<style>
table {{
  border-collapse: collapse;
}}

table, th, td {{
  border: 1px solid black;
  padding: 5px;
}}

img {{
  max-width: 360px;
  height: auto;
  display: block;
}}

img:hover {{
  cursor: pointer;
}}
</style>
</head>
<body>
{table}
</body>
</html>
"""

# HTMLテーブルのヘッダー
header = ["機能ID", "テストケースID", "Chrome", "Edge", "Firefox"]


# データを保持する辞書
data_dict = {}

# CSVファイルの読み込みとデータの整形
with open(csv_file_path, "r", encoding='utf-8') as csv_file:
    reader = csv.reader(csv_file)
    next(reader)  # ヘッダー行をスキップ
    for row in reader:
        function_id = row[0]
        testcase_id = row[1]
        browser = row[2]
        image_file_name = row[3]
        image_file_path = row[4]
        if function_id not in data_dict:
            data_dict[function_id] = {}
        if testcase_id not in data_dict[function_id]:
            data_dict[function_id][testcase_id] = {"chrome": [], "edge": [], "firefox": []}
        data_dict[function_id][testcase_id][browser].append(image_file_path)

# HTMLテーブルの生成
table_html = ""
for function_id, function_data in data_dict.items():
    table_html += f"<h2>{function_id}</h2>"
    for testcase_id, testcase_data in function_data.items():
        table_html += f"<h3>{testcase_id}</h3>"
        table_html += "<table>"
        table_html += "<tr>"
        for col in header:
            table_html += f"<th>{col}</th>"
        table_html += "</tr>"
        table_html += "<tr>"
        table_html += f"<td>{function_id}</td>"
        table_html += f"<td>{testcase_id}</td>"
        for browser in ["chrome", "edge", "firefox"]:
            table_html += "<td>"
            for image_path in testcase_data[browser]:
                windows_image_path = convert_to_windows_path(image_path)
                table_html += f'<a href="{windows_image_path}" target="_blank"><img src="{windows_image_path}" alt="{browser}" title="{browser}"></a><br>'
            table_html += "</td>"
        table_html += "</tr>"
        table_html += "</table>"
        table_html += "<br>"
        
# HTMLファイルの出力
with open(html_file_path, "w", encoding='utf-8') as html_file:
    html_file.write(html_template.format(table=table_html))

print(f"HTMLファイル'{html_file_path}'を作成しました。")

```

## アウトプット
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/348593/925b304b-b67e-053f-cbd1-de00c55722f8.png)

縦スクロールで、パッと見NGのものがないかの確認が簡単にできるようになりました。

## まとめ

- Webのマルチブラウザテスト(3種類)のエビデンスを横並びで確認するビューア作りました
- パッと見NGのものがないかの確認が簡単にできるようになりました
- 今後は画像の類似度判定を追加するなど、よりチェックが簡単になる仕組みを追加したいです