Puppeteerを使ってCSSのテストをしてみる

[Webスクレイピング Advent Calendar 2017](https://adventar.org/calendars/2105) 22日目の記事です。
（内容的にWebスクレイピングからちょっとずれてしまいましたが、気にしたら負けだと思いました）

# 何故Puppeteerを使うと良いのか？

[17日目の記事](https://gist.github.com/sys9kdr/477d4c44b51c722331951c3f4b0b0c13)の方も書かれているように、最近、Headless Chromeの[DevTools Protocol](https://chromedevtools.github.io/devtools-protocol/)を使ってWebスクレイピングが出来る[Puppeteer](https://github.com/GoogleChrome/puppeteer)(綴りが難しい)というライブラリが人気のようです。

以前からあるWebスクレイピングのツールとしては[PhantomJS](https://github.com/ariya/phantomjs)があり、CSSのテストツールとしてもこれを使った[PhantomCSS](https://github.com/Huddle/PhantomCSS)というものがあります。しかしPhantomCSSは

- 依存しているライブラリである[PhantomJSのメンテナンスが終了](http://kytiken.hatenablog.com/entry/2017/11/29/175814)
- それに伴い、[PhantomCSSのメンテナンスも終了](https://github.com/Huddle/PhantomCSS#why-is-this-project-no-longer-maintained)
- ~~PhantomJSでは(おそらく?)DOMレンダリング後のwidth, heightといった値が取れない~~[^1]

[^1]: 試してみたら取れた

という理由から、今後使い続けていくのが難しくなりつつあります。

そこで、新しく登場したPuppeteerで、実際にレンダリングされた後のwidthやheightを使ってCSSのテスト(CSSに書いた値が実際の見た目と合っているかどうか)をしようというのがこの記事です。

Puppetterでスクレイピングしてきてwidthやheightの情報を抽出するのはこちらの記事が大変参考になります。
ref: [PuppeteerでWebページからメインコンテンツっぽいところを抽出してみる](https://qiita.com/ukyo/items/287169de85576dfed608)

# 簡単なサンプル

コード全文は[こちら](https://github.com/yudetamago/puppeteer_css_test)

(本当に簡単な例だけなのでアレですが)例えば以下のように

```html
<!-- html -->
<div id="main">
  メイン
</div>
```

```css
/* CSS */
#main {
  display: inline;
  width: 100px;
  height: 100px;
}
```

inline要素に間違ってwidthやheightを指定してしまった場合でも、puppeteerを使って


```javascript
/* javascript */
const puppeteer = require('puppeteer');
(async() => {
  const url = "http://127.0.0.1:8080/"
  const browser = await puppeteer.launch();
  const page = await browser.newPage();
  await page.goto(url, {waitUntil: 'networkidle2'}); // ページがレンダリングされるまで待つ
  const selector = '#main'
  await page.waitForSelector(selector); // DOMが表示されるまで待つ
  const rect = await page.evaluate(selector => {
    const element = document.querySelector(selector);
    const {x, y, width, height} = element.getBoundingClientRect();
    return {x: x, y: y, width: width, height: height}
  }, selector);
  console.log(rect) // デバッグ用にとりあえずログに出す
  await page.screenshot({path: 'ss.png', clip: rect}) // 一応目視でも確認するためのスクリーンショット
  await browser.close();
})();
```

のようなコードを書いて実行すると、CSSで指定した値そのものではなく、実際にレンダリングされた後の値が返ってきて、間違いであることを検証出来ます。

実行例

```sh
$ node script.js 
{ x: 8, y: 12, width: 48, height: 17 }
```

ss.png(inline): ![ss.png](https://qiita-image-store.s3.amazonaws.com/0/15832/9a41c75b-0e1d-3753-bd69-9b34eb85a870.png)

念のため比較用に、CSSを`display: block`にしてから再度実行すると

```sh
$ node script.js 
{ x: 8, y: 8, width: 100, height: 100 }
```

ss.png(block): ![ss.png](https://qiita-image-store.s3.amazonaws.com/0/15832/f0736813-01d6-6763-c82b-6ac991b626c6.png)

のように、ちゃんと意図通りにレンダリングされた結果の値が返ってきます。

# 余談

PhantomJSで出来ないことをやりたかったけど、PhantomJS試してみたら色々出来てしまったので微妙な感じになってしまった、うーん...

# 参考文献
- PhantomCSS
    - [PhantomCSSを利用したビジュアルリグレッションテスト - WPJ](https://www.webprofessional.jp/visual-regression-testing-with-phantomcss/)
    - [PhantomCSSでViewの回帰テスト](https://qiita.com/awakia/items/faf502259efba09df7a5)
- Puppetter
    - [動的に生成されたページをpuppeteerでスクレイピングする](https://qiita.com/Climber22/items/556d4b05b50ae8128788)
    - [PuppeteerでWebページからメインコンテンツっぽいところを抽出してみる](https://qiita.com/ukyo/items/287169de85576dfed608)