chromeless on Lambda を試してみる

最近は、E2E テストをどうにかできないかと思って、色々探っています。そのなかで、個人的にアツいなと思った chromeless の紹介と試しに使ってみたレポートを行いたいと思います。

僕が探した限りだと、現時点(2017/10/23)で日本語の記事は、githubのTrendingになっているchromelessをWindowsで試すの1件のみに思えました。

https://github.com/graphcool/chromeless

AWS Lambda 上で Headless Chrome を動作させ、そちらに対してリクエストを行うということで、ブラウザテスト実行の並列性を 1000 まで上げるというアプローチを行うのがこちらのライブラリの目指すところです。

chromeless の README にも記載がありますが、AWS Lambda 上で動作させるために、AWS のアカウントが必要になります。

image

Lambda 上で動作させるモードと Local で動作させるモードのふたつがあり、 Lambda 上で動作させるモードは Proxy 利用となります。

大まかな手順としては、以下のようになります。

1. chromeless リポジトリを clone し、Proxy 用のサービスのディレクトリで npm install2. コンフィグを設定し、自分のアカウントの Lambda 上に function をデプロイ3. デプロイが完了すると、endpoint の url と api key が発行される4. 発行された endpoint, api key を利用し、proxy に対してリクエストする

手順はこちら。

https://github.com/graphcool/chromeless/tree/master/serverless#chromeless-proxy-service

ひとつハマりどころとしては、全サービス (AWS IoT, AWS Lambda, AWS S3) のリージョンは揃えておく必要があります。

% export AWS_PROFILE=dev
 
% npm run deploy
 
 
> chromeless-remotechrome-servie@1.3.0 deploy /Users/serima/chromeless-lambda/chromeless/serverless
> serverless deploy
 
Serverless: Compiling with Typescript...
Serverless: Using local tsconfig.json
/Users/serima/chromeless-lambda/chromeless/serverless/node_modules/@types/graphql/subscription/subscribe.d.ts (17,4): Cannot find name 'AsyncIterator'.
/Users/serima/chromeless-lambda/chromeless/serverless/node_modules/@types/graphql/subscription/subscribe.d.ts (29,4): Cannot find name 'AsyncIterable'.
Serverless: Typescript compiled.
Serverless: Injecting Headless Chrome...
Serverless: Packaging service...
Serverless: Excluding development dependencies...
Serverless: Creating Stack...
Serverless: Checking Stack create progress...
.....
Serverless: Stack create finished...
Serverless: Uploading CloudFormation file to S3...
Serverless: Uploading artifacts...
Serverless: Uploading service .zip file to S3 (74.27 MB)...
Serverless: Validating template...
Serverless: Updating Stack...
Serverless: Checking Stack update progress...
..........................................................................................
Serverless: Stack update finished...
Service Information
service: chromeless-serverless
stage: dev
region: ap-northeast-1
stack: chromeless-serverless-dev
api keys:
  dev-chromeless-session-key: xxxxxxxxxxxxxxxxxxx
endpoints:
  GET - https://xxxxx.execute-api.ap-northeast-1.amazonaws.com/dev/version
  OPTIONS - https://xxxxx.execute-api.ap-northeast-1.amazonaws.com/dev/
  GET - https://xxxxx.execute-api.ap-northeast-1.amazonaws.com/dev/
functions:
  run: chromeless-serverless-dev-run
  version: chromeless-serverless-dev-version
  session: chromeless-serverless-dev-session
  disconnect: chromeless-serverless-dev-disconnect

この状態で、Lambda のマネジメントコンソールを確認してみると、たしかに 4 つの関数が追加されています。

image

さて、次はこちらの API に対してリクエストを投げてみたいと思います。ここでは試しに iPhone の UserAgent と ViewPort を設定してみています。

const Chromeless = require('chromeless').default
 
async function run(url) {
  const chromeless = new Chromeless({
    remote: {
      endpointUrl: 'https://xxxxx.execute-api.ap-northeast-1.amazonaws.com/dev',
      apiKey: 'xxxxxxxxxxxx'
    },
  })
 
  const screenshot = await chromeless
    .setUserAgent('Mozilla/5.0 (iPhone; CPU iPhone OS 7_0 like Mac OS X) AppleWebKit/537.51.1 (KHTML, like Gecko) Version/7.0 Mobile/11A465 Safari/9537.53')
    .setViewport({width: 640, height: 1136, scale: 1})
    .goto(url)
    .screenshot()
 
  console.log(url)
  console.log(screenshot) // prints local file path or S3 url
 
  await chromeless.end()
}
 
run('https://www.yahoo.co.jp').catch(console.error.bind(console))
run('https://www.google.com').catch(console.error.bind(console))
run('https://www.microsoft.com').catch(console.error.bind(console))
run('https://www.facebook.com').catch(console.error.bind(console))

こちらを実行してみますと、ほどなくして以下のような出力があらわれます。

% time node run.js
https://www.yahoo.co.jp
https://xxxxx-ap-northeast-1-chromeless.s3.amazonaws.com/cj946gjq6000001oy1bc2zzvg.png
https://www.microsoft.com
https://xxxxx-ap-northeast-1-chromeless.s3.amazonaws.com/cj946gjrb000001m9nxomxprr.png
https://www.google.com
https://xxxxx-ap-northeast-1-chromeless.s3.amazonaws.com/cj946gk23000001qg2zg47pe9.png
https://www.facebook.com
https://xxxxx-ap-northeast-1-chromeless.s3.amazonaws.com/cj946gk7n000001oydipvndx1.png
node run.js  0.48s user 0.16s system 4% cpu 13.322 total

s3 にスクリーンキャプチャが自動的にアップロードされ、その URL が返ってきているのがわかります。

早速開いてみますと、ViewPort がちゃんと反映はされているものの、文字化けというか日本語が表示されるであろう箇所が豆腐になっています…。

image
image
image
image

ちなみに、 https://www.yahoo.com/ を指定してみると

image

ちゃんと出ています。

やはり日本語が表示されないようです。その後いろいろと調べていると、chromeless が依存している serverless-chrome に日本語フォントがインストールされていないため、日本語は表示されないという状況のようです。

その issue は こちら

こちらの issue にもコメントされていますが @fd0 さんのブログに「serverless-chrome で日本語を表示できるようにする」というエントリがあり、できないことはないようです。(lambda の 50MB 制限を超えないようにフォントをインストールした chrome のバイナリを strip して容量を減らすというの、面白かった)

フォント問題は意外と根が深いのかもな…と思いつつ、本家の対応を待つかという気持ちになりつつあります。

ちなみに、chromeless にももちろん click(selector: string) や type(input: string, selector?: string) などの Chrome でのメソッドが使用できます。(一部未実装があるようですが)一般的に E2E テストは実行時間が遅いという認識ですが、Lambda を利用して並列数を爆発的に増やすというアプローチはすごく面白いので、今後も非常に期待しています。