WebBluetoothでは基本的にはPromiseを利用する必要がありますが、Async/Awaitが利用できるようになりました。
これにより可読性の高いコードが書けるようになります。
node.jsでサブディレクトリまで一度に作成してくれるmkdirpが便利
node.jsでディレクトリを再帰的に作成してくれるモジュールです。
"mkdir -p"コマンドを実行するのと同じです。
インストールは簡単。
npm install mkdirp
ディレクトリを作成するサンプルです。
const mkdirp = require('mkdirp'); mkdirp('./foo/bar/baz', err => { if (err) console.error(err) else console.log('success!') });
地味に便利です。
github.com
WebBluetoothAPIのW3Cドキュメントを翻訳しました
Web Bluetooth API 日本語ドキュメント
https://tkybpp.github.io/web-bluetooth-jp/
WebBluetoothAPIとは
このAPIは、ブラウザ上からBluetoothLE(BLE)デバイスを操作することが出来るAPIです。
jsでスクリーンショットを比較してデザインデグレの自動検出をする
E2Eテストの自動化にトライしてみました。
今回はUIの細かい挙動のテストではなく、デザインのデグレ検出にフォーカスをしています。
Webデザインにおいて1pxへのこだわりは非常に重要です。大規模なサービスになると、1pxの違いで数億円の売上に影響することも珍しくありません。
www.nikkei.com
複数人のチームで開発を進めていて、意図せずデザインのデグレが発生していまうことがあります。この時、1px単位のごくわずかな差分の場合、肉眼では見落としてしまいがちです。しかしながら1pxの差が事業に影響を与える可能性があるので、デザインのデグレは防ぐ必要があります。
今回はUIの差分を自動検知する簡易ツールを作成してみます。
nightwatch.jsとresemble.jsを利用します。
・Nightwatch.js | Node.js powered End-to-End testing framework
・Resemble.js : Image analysis
実装に入る前に、どれほどの精度で差分が検出できるのかお見せします。
nightwatch.jsの公式サイトでサンプルを用意してみました。
(今回はサンプルのため意図的に変更しています。)
beforeとafterで5箇所の差分が発生していますが、わかりますか?
# before
# after
いかがでしょうか?
ここで、beforeとafterの画像をresemble.jsを使って比較をしてみます。
そうすると、2つの画像の差分をこのように検出することができるのです。
# answer
5つの差分は
- 左上のロゴの位置が2pxずれた
- 右上のHomeリンクからアンダーラインが消えた
- 画面中央の「Write End-to-End〜」の文章がボールドになった
- Downloadボタンの幅が狭くなった
- 画面下部の「Nightwatch.js is an easy〜」の文章のフォントカラーが変わった
でした。
細かい。
こんな細かい差分なら放っておいてもいいじゃん、という人もいます。
しかしながら、インターネットの世界では1pxの差分が数億円を左右することに繋がるのです。
とはいえ、こんな細かいところまで差分が無いか人力で確認しようとすればとても労力が必要になります。
肉眼だと気付きにくい差分でも自動化してしまえば一瞬ですね。
実装
2つの要素から構成しています。
nighwatch.js → 画面のスクリーンショットを保存する
resemble → スクリーンショットを比較して差分を検出する
スクリーンショットの保存はnighwatch.jsを利用すれば簡単にできます。
対象のURLを指定して、saveScreenshotを実行するだけです。
'Sample test' : function (browser) { browser .url('http://nightwatchjs.org/') .saveScreenshot('example.png') .end(); }
これを日次やコミット毎など定期的に実行するようにすれば便利です。
そして、画像ファイルの比較はresembleを利用してこのように書きます。
const fs = require('fs'); const resemble = require('node-resemble-js'); const file1 = Buffer(fs.readFileSync('file_path_to_before.png')); const file2 = Buffer(fs.readFileSync('file_path_to_after.png')); resemble.outputSettings({ transparency: 0.1 }); resemble(file1).compareTo(file2).onComplete( data => { if(data.misMatchPercentage >= 0.01) { data.getDiffImage().pack().pipe(fs.createWriteStream('file_path_to_diff.png')); } });
比較結果はonComplete()の中で扱うことが出来ます。
差分の割合がmisMatchPercentageとして格納されているので、一定の割合で差分があった場合のみ処理をすることも簡単に出来ます。
また、差分の画像データもgetDiffImage()を利用して取得できるので、外部ファイルとして保存することが出来ます。
Node.jsを使って静的コンテンツを表示するHTTP/HTTPSサーバーを用意する
connectモジュールを使えば、簡単にHTTPサーバーを作成できます。
connectはNode.js 向けの拡張可能な HTTP サーバフレームワークです。ミドルウェアと呼ばれるプラグイン機構をつかって HTTP サーバを拡張することが出来ます。
これを包括する形でルーティングなどを簡単にしてくれるのがexpressですね。
カレントディレクトリをルートディレクトリとするサンプルを作ってみます。
ローカルで簡単に動作確認などテストを実施したい時に便利です。
const http = require('http'); const connect = require('connect'); const logger = require('morgan'); const serveStatic = require('serve-static'); const app = connect() .use(logger('dev')) .use(serveStatic(process.cwd())) http.createServer(app).listen(3000);
serveStaticはローカルに存在するファイルを返すミドルウェアです。
connectのv2.xでは、staticプロパティとしてconnectに組み込まれていましたが、v3.0から分離されています。
同じくloggerもmorganと名前を変えて分離されています。
これだけで完了です。
続いてHTTPSサーバーを用意してみます。
まずはSSLに必要な鍵を生成します。
$ openssl genrsa -out localhost.key 2048 $ openssl req -new -x509 -key localhost.key -out localhost.cert -days 3650 -subj /CN=localhost
適当にapp配下にkeysディレクトリを作成し、生成されたkeyとcertを移動します。
あとはこの生成した2つのファイルを読み込んでオプションとして渡してあげるだけでHTTPS対応が完了します。
const https = require('https'); const fs = require('fs'); const connect = require('connect'); const logger = require('morgan'); const serveStatic = require('serve-static'); const options = { cert: fs.readFileSync('./keys/localhost.cert'), key: fs.readFileSync('./keys/localhost.key') }; const app = connect() .use(logger('dev')) .use(serveStatic(process.cwd())) https.createServer(options, app).listen(3000);
確認してみましょう
$ node server.js
$ curl 'https://localhost:3000/app/' -vk * Trying localhost... * Connected to localhost (localhost) port 3000 (#0) * TLS 1.2 connection using TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 * Server certificate: localhost > GET /app/ HTTP/1.1 > Host: localhost:3000 > User-Agent: curl/7.43.0 > Accept: */* > < HTTP/1.1 200 OK < Accept-Ranges: bytes < Cache-Control: public, max-age=0 < Last-Modified: Thu, 07 Apr 2016 14:39:04 GMT < ETag: W/"62e-153f1298740" < Content-Type: text/html; charset=UTF-8 < Content-Length: 1582 < Date: Fri, 06 May 2016 16:17:05 GMT < Connection: keep-alive < <!doctype html> <html> <head> . .
ステータスコードは200となっており、HTMLファイルの中身も表示されていることが確認できます。
もちろんブラウザで https://localhost:3000/app/ にアクセスしても表示されます。
存在しないファイルにアクセスするときちんと404を返してくれます
$ curl 'https://localhost:3000/NotFound/' -vk * Trying localhost... * Connected to localhost (localhost) port 3000 (#0) * TLS 1.2 connection using TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 * Server certificate: localhost > GET /not_found/ HTTP/1.1 > Host: localhost:3000 > User-Agent: curl/7.43.0 > Accept: */* > < HTTP/1.1 404 Not Found < X-Content-Type-Options: nosniff < Content-Type: text/html; charset=utf-8 < Content-Length: 23 < Date: Fri, 06 May 2016 16:19:35 GMT < Connection: keep-alive < Cannot GET /NotFound/
Node.jsからgitコマンドを実行してコミットログなどを取得する
前回はnode.jsからシェルコマンドを実行する方法を紹介しました。
tkybpp.hatenablog.com
これを利用することで、gitのログなども取得できるようになります。
git logコマンドを実行して最新のコミットIDと日付を取得してみます。
const execSync = require('child_process').execSync; const cmd = 'git log -n 1 --format=%H,%cd'; const result = execSync(cmd).toString().split(','); const commitID = result[0]; const commitDate = new Date(result[1]); console.log(commitID); console.log(commitDate);
$ node test.js e12e8a92d3469ac3d9cfeeg0cbxb2c2b3aa7d441 Sun Apr 24 2016 09:34:58 GMT+0100 (CET)
今回は最新の1つのみを取得しましたが、git logのオプションは他にもあります。
formatも自由に記述できるので、使いこなせると便利かもしれません。
Git - git-log Documentation
エンジニアのためのGitの教科書 実践で使える!バージョン管理とチーム開発手法
- 作者: 株式会社リクルートテクノロジーズ,株式会社リクルートマーケティングパートナーズ,河村聖悟,太田智彬,増田佳太,山田直樹,葛原佑伍,大島雅人,相野谷直樹
- 出版社/メーカー: 翔泳社
- 発売日: 2016/01/19
- メディア: Kindle版
- この商品を含むブログ (1件) を見る
Node.jsからシェルコマンドを実行する
node.jsからシェルコマンドを実行するにはexecを使えば簡単にできます。
const exec = require('child_process').exec; exec('ls -la ./', (err, stdout, stderr) => { if (err) { console.log(err); } console.log(stdout); });
exec関数は非同期関数であり、callbackに渡されるのは、err、標準出力文字列、標準エラー出力文字列です。
また、同期的に処理を実行したい場合にはexecSyncが利用できます。
const execSync = require('child_process').execSync; const result = execSync('ls -la ./'); console.log(result);
execSyncの返り値はBufferなので注意しましょう。
$ node test.js <Buffer 74 6f 74 61 6c 20 38 30 0a ... >
出力結果を処理したい場合にはtoString()などを利用しましょう。
const result = execSync('ls -la ./').toString();
$ node test.js total 10 drwxr-xr-x 17 user test 578 4 24 17:17 . drwxr-xr-x 14 user test 476 4 17 05:02 .. -rw-r--r--@ 1 user test 6148 3 27 10:49 file1 -rw-r--r-- 1 user test 188 1 21 12:55 file2 . .
詳しい仕様は公式ドキュメントを参照ください。
Child Process Node.js v6.1.0 Manual & Documentation
サーバサイドJavaScript Node.js入門 (アスキー書籍)
- 作者: 清水俊博,大津繁樹,小林秀和,佐々木庸平,篠崎祐輔,高木敦也,西山雄也,Jxck
- 出版社/メーカー: KADOKAWA / アスキー・メディアワークス
- 発売日: 2014/02/27
- メディア: Kindle版
- この商品を含むブログを見る
実践Node.js プログラミング (Programmer's SELECTION)
- 作者: Mike Cantelon,Marc Harter,T.J. Holowaychuk,Nathan Rajlich,生越昌己,吉川邦夫
- 出版社/メーカー: 翔泳社
- 発売日: 2014/06/10
- メディア: 大型本
- この商品を含むブログ (1件) を見る