WebdriverIO で動画を撮影しアニメ GIF にする
WebdriverIO v5 には Video Reporter があるので動画の撮影が可能です。
まず MP4 動画を生成してアニメ GIF を作成することになりますが、その際 MP4 そのものではなく MP4 を生成するのに使用された PNG を使ってアニメ GIF を作成したいと思います。
これによりある程度 GIF アニメのサイズを削減できます。
生成例
これは前回の WebdriverIO でファイルの添付・ペースト・ドロップをテストする - Qiita を撮影したものです。
時間での区切りではなく、各 setValue
などのイベントでのコマ送りになります。
Video Reporter のインストール
module のインストール
Video Reporter ページ上の指示に従いインストールします。
yarn add wdio-video-reporter
# or
npm install wdio-video-reporter
wdio.conf.js
編集
成否に関わらず動画を残したいので saveAllVideos: true
とし、普通のレポートも出しておきたいので spec
は残しておきます。
// 追加
const video = require('wdio-video-reporter');
exports.config = {
// 略
reporters: [
'spec',
// 追加
[video, {
saveAllVideos: true,
videoSlowdownMultiplier: 3,
}]
],
// 略
}
これでテストごとに動画が撮影されるようになりました。
アニメ GIF 生成
スクリプト全景
const fs = require('fs');
const path = require('path');
const glob = require('glob');
const execSync = require('child_process').execSync;
const movies = path.join(__dirname, './_results_');
const raws = path.join(movies, 'rawSeleniumVideoGrabs');
const animations = path.join(__dirname, './animations');
function fromMp4() {
prepare();
glob.sync(path.join(movies, '*.mp4')).forEach(function (mp4) {
const name = path.basename(mp4, '.mp4');
const gif = path.join(animations, `${name}.gif`);
execSync(`ffmpeg -i ${mp4} -filter_complex "[0:v] fps=10,setpts=PTS/0.25,scale=640:-1,split [a][b];[a] palettegen [p];[b][p] paletteuse" ${gif}`)
});
}
function fromPng() {
prepare();
glob.sync(path.join(raws, '*')).forEach(function (dir) {
const name = path.basename(dir);
const gif = path.join(animations, `${name}.gif`);
execSync(`ffmpeg -r 1 -i ${dir}/%04d.png -filter_complex "[0:v] scale=640:-1,split [a][b];[a] palettegen [p];[b][p] paletteuse" ${gif}`)
});
}
function prepare() {
clearAnimations();
fs.existsSync(animations) || fs.mkdirSync(animations);
}
function clearAnimations() {
glob.sync(path.join(animations, '**/*.gif')).forEach(fs.unlinkSync);
}
function clearMovies() {
glob.sync(path.join(raws, '**/*.png')).forEach(fs.unlinkSync);
glob.sync(path.join(raws, '*')).forEach(fs.rmdirSync);
glob.sync(path.join(movies, '**/*.mp4')).forEach(fs.unlinkSync);
}
function clear() {
clearAnimations();
clearMovies();
}
switch (process.argv[2]) {
case '-p':
return fromPng();
case '-m':
return fromMp4();
case '-c':
return clear();
default:
console.error('require -p to encode from png, -m to encode from mp4 or -c to clear movies');
}
Video Reporter の生成するファイル
デフォルトでは _results_
というディレクトリに MP4 が生成されます。そしてそのディレクトリの中の rawSeleniumVideoGrabs
に元となった PMG が残されています。
const movies = path.join(__dirname, './_results_');
const raws = path.join(movies, 'rawSeleniumVideoGrabs');
PNG -> GIF
execSync(`ffmpeg -r 1 -i ${dir}/%04d.png -filter_complex "[0:v] scale=640:-1,split [a][b];[a] palettegen [p];[b][p] paletteuse" ${gif}`);
ffmpeg には PNG からアニメ GIF を作成する機能があるのでそれを使用しています。
MP4 -> GIF
execSync(`ffmpeg -i ${mp4} -filter_complex "[0:v] fps=10,setpts=PTS/0.25,scale=640:-1,split [a][b];[a] palettegen [p];[b][p] paletteuse" ${gif}`);
同じく ffmpeg の機能そのままですが、パレット抽出のオプションには ffmpegでとにかく綺麗なGIFを作りたい - Qiita を参考にさせていただきました。
その他
Video Reporter はファイルをどんどん積んでいくので、消去用のスクリプトを用意しました。
package.json
編集
各動作を yarn gif:foo
で呼べるようにしておきます。
{
// 略
"scripts": {
"gif:clear": "node ./encode.js -c",
"gif:png": "node ./encode.js -p",
"gif:mp4": "node ./encode.js -m",
"test": "wdio wdio.conf.js"
},
// 略
}
まとめ
これで動画は GIF のみを許可している場所でも操作状況をシェアできるようになりました。