Edited at

Mapbox GL JS 仕様のベクトルタイルが想定どおりに表示されているかどうかを CircleCI でテストする

みなさんはじめまして。TileCloud というサービスの開発チームの宮内です。

https://tilecloud.io/

TileCloud とは、OSM ベースの地図をベクトルタイルで配信するための地図専用ホスティングのサービスで、ただいまパブリックベータを少しでも早く公開できるように、せっせと頑張っております。

このサービスでは、OSM のデータをベースに Mapbox GL JS と互換性のあるベクトルタイルをご提供しようと思っていまして、Mapbox GL JS のプラグインや周辺ツールなどをオープンソースで開発中です。

https://github.com/orgs/tilecloud/dashboard

今回の記事では、これらのツール類を開発する際の基盤技術ともいえる地図の動作確認の CI 方法についてざざっとかいつまんで説明していきます。


CI でやりたいこと

僕たちが今作っているものは、主にサービスのダッシュボードで使用することを想定しているもので、例えば以下のようなものがあります。

https://github.com/tilecloud/mbgl-export-control

これは、Mapbox GL JS のプラグインで、これを有効化すると、以下のように表示中の地図をダウンロードするためのボタンが表示されるというものです。

ダウンロードされる地図は PNG フォーマットで、ブラウザの解像度よりも高い 300dpi という解像度でもダウンロードできるので印刷にフレンドリーなのと、© OpenStreetMap Contributors というアトリビューションが自動的に入ります。

他にも

などのモジュールがあり、まだまだ増える予定です。

これらひとつひとつは小ネタなのですが、これらをオープンソース化しつつ複数組み合わせてリッチなダッシュボードにしていくというアーキテクチャにしようとしているため、単品で動作することももちろん重要ですが、モジュール同士のコンフリクトなどもありえるため、最低でも地図が表示されていることだけは自動的にチェックしたいと考えました。

もしモジュール間のコンフリクトがあればそのときにそれも自動化テストの中に追加する感じですね。


Puppeteer を使用した自動化テスト

実際に地図が表示されていることを確認するには、サービス全体のさまざまな要素すべてが健全に動作している必要があります。

たとえば、僕たちのサービスでは地図の表示の際にAPIキーが必要なんですが、そのための認証用のAPIが動作しているとかですね。

逆に言えば地図の表示確認を自動化できれば、それらのAPIの動作確認も完璧ではないにしろある程度できることになります。

const chai = require('chai')

const assert = chai.assert
const puppeteer = require('puppeteer');

describe('Tests for Maps.', () => {
it(`The map should be displayed as expected`, done => {
(async () => {
const browser = await puppeteer.launch()
const page = await browser.newPage()
await page.goto('https://xxxx.tilecloud.io/')
await setTimeout(async () => {
await page.screenshot({path: 'screenshots/osm-bright.png'})
const map = await page.$('#map div:first-child')
const className = await (await map.getProperty('className')).jsonValue();
assert.deepEqual('mapboxgl-canary', className)
await browser.close()
done()
}, 5000)
})();
});
})

上のソースは、実際に地図が表示されているかどうかを Puppeteer というヘッドレスブラウザを使って確認するための例です。

この例では https://xxxx.tilecloud.io/ という URL に対してヘッドレスブラウザでアクセスし、JS が無事に動作したときに生成されるであろう要素の存在有無(今回は .mapboxgl-canary というクラス属性をもった DIV 要素の有無)を確認して、さらにスクリーンショットを保存しています。

まだそこまで頑張っていませんが、スクリーンショットについては、いろんなパターンの地図のギャラリー的なサイトをつくってそこでざざっと俯瞰して確認できるようにしようかなと思っています。


CI サービスで自動化

現時点で僕たちは52通りの地図の動作確認をしているのですが、これらは今後バグや不具合等の経験を通してどんどん増えていくと思います。

そのため CI サービスを利用して Git のコミットなどをトリガーにしてテストの実行そのものも自動化するわけですが、Mapblox GL JS ならではのハマりポイントがありました。

なんと Trais CI で起動するコンテナで Puppeteer を起動しても、Mapbox GL JS ベースの地図がうまく表示されません。

Mapbox GL JS は、地図を表示するために WebGL を使用しているのですが、どうやらそのあたりに制限があるようですね。(GPU が利用できないとかそういうこと?どなかた詳しい人解説を希望します。)

一瞬うぎゃーとなりましたが救いの神がいました。 Circle CI さんですね。

というわけで Mapbox GL JS のレンダリングをヘッドレスブラウザで行う際は、Circle CI でどうぞ。

ちなみに Lambda でも挑戦したのですが、Puppeteer のバイナリが Lambda のファイルサイズの制限にひっかかってしまって、やはりうまくいきませんでした。