phpでSNSとかのシェア画像を動的に出し分けしてnode/chromelessで自動テスト+確認用にスクショ出力する

node

概要

chromeless使っての自動シェアテスト作ったので適当な所多いにあると思いますがサンプル的においてみます。

昔やった案件でVue.jsでページ内の画像切替。
※内容としてはTシャツとかのデザイン切り替え/着せ替えするようなイメージです
ユーザーの選択によってシェアURL&画像動的に切り替えるようなコンテンツの制作しました。

で、その選択&結果の数が3色×3タイプ×12パターンみたいな感じで結構なボリュームで確認するの大変なので自動でシェア画像のチェックできませんか?

というご相談頂き、自動テストとかあんまりやったことないけどURL飛んでスクショ撮るぐらいなら多分できると思いますよって感じでお引き受けしました。

やり方として合っているのかは不明、多分もっとスマートな方法はありそうかと思いますが

自動テスト用ツール

chromelessって初めて使ったのだけれども、自動テスト用のツールって認識でよいのかな?

自動テストも初めて。

PhantomJSとかと同じようなくくり?だと思う、PhantomJSもよくわかってないので多分。

Vue.jsとshare.phpでシェア画像の切り替え

Vueデータバインディング

メインのコンテンツ側はVueと書きつつ

別にURL変えればVueでなくても何でもでもいいと思うのですが

{{ shareurl }}

とかのデータバインディングでURL変えるだけです。

share.php

シェア用のphpは下記みたいな感じ。

<?php
header("Content-type: text/html; utf-8");

$color = @$_GET['color'];
$type = @$_GET['chara'];
$code = @$_GET['design'];
$is_test = @$_GET['test'];
// パラメーター
// $is_testでシェアなのかテストなのか判定

$query = urldecode($_SERVER["QUERY_STRING"]);

$base = (empty($_SERVER['HTTPS']) ? 'http://' : 'https://').$_SERVER["HTTP_HOST"];
$path = "/share/";
$imagedir = "images/";
$filename = $color."-".$chara."-".$design.".png";

$request = $base.$_SERVER["REQUEST_URI"];
$og_url = $base.$path.'?'.$query;
$og_img = $base.$path.$imagedir.$filename;
// 実際出すファイルとかURLとか決める
?>

<!DOCTYPE html>
<html>
<head>
  <meta charset="utf-8">
  <title>タイトル</title>
  <meta name="description" content="description">
  <meta name="keywords" content="keywords">
  <meta property="og:site_name" content="og:site_name">
  <meta property="og:title" content="og:title">
  <meta property="og:locale" content="ja_JP">
  <meta property="og:description" content="og:description">
  <meta property="og:type" content="article">
  <meta http-equiv="Pragma" content="no-cache">
  <meta name="twitter:card" content="summary_large_image">
  <meta property="og:url" content="<?php echo $og_url ?>">
  <meta property="og:image" content="<?php echo $og_img ?>">
</head>
<body>
  <?php if(!$is_test): ?>
    <!-- 
      テストでない場合は遷移 
      ちなみjsで遷移でないとシェアが正しく動作しなかった気がする
      http-equiv="Refresh"とかは駄目だったような、多分
    -->
    <script type="text/javascript">location.href = "<?php echo $base ?>";</script>
  <?php else: ?>
    <!-- テスト時の内容 -->
    <script>
      var el = document.createElement('div');
      el.id = "shareimg";
      var image = new Image();
      image.onload = function(){
        el.classList.add("true");
        image.width = "600";
        document.querySelector(".main").appendChild(el);
        el.appendChild(image);
      }
      image.onerror = function() {
        el.classList.add("false");
        document.querySelector(".main").appendChild(el);
        el.appendChild(image);
      }
      image.src = '<?php echo $og_img; ?>';
    </script>
    <div style="text-align: center" class="main">
      <p><?php echo $request; ?></p>
    </div>
  <?php endif; ?>
</body>
</html>

引っ張ってきたもの少し整理しただけなので雑かもですが

一応本題であるテストのポイントとしては場合body開始直後に書いてるjs周りです。

#shareimg要素の配置とかonload/onerrorでクラス付加してます。

テストの環境構築

chromelessはnodeが必要なので,nodeってなんぞって方はnodeもインストールが必要です。

yarn(npm)で諸々addします。

 yarn add chromeless mocha chai dotenv --dev

chromeless

今回のメイン、ヘッドレスブラウザです。ヘッドレスとはGUI が無いブラウザ。らしいです。

mocha/chai

これも今回で初めて触ってみたのだけども。テストコード用のなんかです。

アサーションツール?っていう言い方で良い?

describeはmochaでassertはchaiぐらいが一応今回わかった。

dotenv

別に無くてもいいのだけれど何となくつかってみる。環境変数用、だと思う。

test.js(シェアテストとスクショ撮る)

const path = require('path');
const { Chromeless } = require('chromeless')
const chai  = require('chai');
const dotenv  = require('dotenv').config();

const BASE = process.env.SHARE_TEST_URL || "http://test.com";
// .envにSHARE_TEST_URLいれておけばそっちが優先されるハズ。

const COLOR = ["r","g","b"];
const TYPE = ["a","b","c"];
const CODE = [...Array(12).keys()].map(i => ++i);

const TIMEOUT = 3000;
const chromeless = new Chromeless();

describe('ogimageの', function() {
	context('画像ファイルありますのん?ってテスト', function() {
		(async () => {
			await new Promise((resolve, reject) => {
				COLOR.forEach((_color, _colorIndex) => {
					TYPE.forEach((_type, _typeIndex) => {
						CODE.forEach((_code, _codeIndex) => {
							it(`${_color}-${_type}-${_code}は?`, async function() {
								this.timeout(TIMEOUT);

								let url = BASE + `?color=${_color}&chara=${_type}&design=${_code}` + "&test=1";

								let result = await chromeless
								.goto(url)
								.wait('#shareimg')
								.screenshot({filePath: path.join(__dirname , 'result/_result__' + `${_color}-${_type}-${_code}` + ".png") })
								.evaluate(() => {
									return ((document.getElementById('shareimg').className === "true") ? true : false);
								})

								chai.assert.isTrue(result);
								if(_colorIndex == COLOR.length - 1 && _typeIndex == TYPE.length - 1 && _codeIndex == CODE.length - 1) resolve();
							});
						})
					})
				})
			})
			await chromeless.end();
		})();
	})
})

上記がシェア画像のテストとスクショする本文です。

describeとcontextが良く分からないのだけれども

一応describe(クラス)/context(メソッド)というルールらしいのだけど今回みたいな場合はどうすればよいのか不明。

一応動作はするのでおまじないと思って書きます。

async/await慣れとかないと思って使ってみましたが変な所あるかもです。

chai.assertってのがあるとコンソールにエラーの内容らしき物が出力されます。

抜粋でメインの部分の軽い説明

let result = await chromeless
.goto(url) //urlに移動して
.wait('#shareimg') // appendChildされるまで待つ
.screenshot({filePath: path.join(__dirname , 'result/_result__' + `${_color}-${_type}-${_code}` + ".png") }) 
// まったらスクショ撮る 場所はfilePathで指定
.evaluate(() => {
        // evalします。share.phpのonload/errorで付加してます。
	return ((document.getElementById('shareimg').className === "true") ? true : false);
})

です。evaluate()の部分が多分chromlessとかにそういうメソッド用意されてるかもですが。

share.php側のonload/errorで付加した真偽利用してます

一応個人的に要件はみたせたと思うのでこれでよしとしてます。

以上です。

webnode

Posted by admin