読者です 読者をやめる 読者になる 読者になる

Thanks Driven Life

日々是感謝

Capybara で within した場所や find したノードに枠を付けてくれるやつ作った

gongo/maizebox · GitHub

まだまだやる事はあるのですが、とりあえず動いたので公開。

What

件名の通り。結果から言うと下図のようにしてくれるやつです。

f:id:gongoZ:20130731233022p:plain

Why

PhantomJSでページに注釈を付けてスクリーンショット撮るやつ - hitode909の日記 という素晴しい記事とツールを発見したのですが 仕事では Capybara 使っている (driver は selenium-webdriver or poltergeist) ので、Capybara から使えると便利だなと思ったので作ってみました。

How (for User)

examples にもあるやつです。

visit 'http://www.apple.com/jp/'

within(:css, 'div.text') do
  page.attention_here
  page.attention_to(find(:css, 'img', match: :first))
end

within(:css, 'aside') do
  within(:css, 'li.first-child') do
    page.attention_here
  end

  within(:css, 'li.third-child') do
    page.attention_to(find(:css, 'img'))
  end
end
  • attention_here
    • 現在いるスコープ (<div> だったり <pre> だったり) に対して枠を付ける用途
  • attention_to(element)
    • find した element (button だったり <input> だったり) に対して枠を付ける用途

上のコードで、先程見せた図のように各要素へ赤枠をつけます。ここら辺の cssweb-overlay を参考にしてます。

How (for Developer)

実装方法いろいろ試して今の形になったんですが、他によさそうな道筋があれば教えてください!!

  1. attention が呼ばれた場所(もしくは指定した node)の locator (xpath or css selector) を取得
  2. 1 でゲットした locator を使い、jQuery.find(locator).css(properties) を実行
  3. 枠できた

現在のスコープの取得方法

ネストした within とか、そのスコープ内で指定した node の path ってどうやればわかるんだろうって思って、 最終的には Capybara::Node::Element が持っていた @query (Capybara::Query) を使用しました。

https://github.com/gongo/maizebox/blob/6dd64ac6979329a5fb3abce62113cc5af926bb64/lib/maizebox/renderer.rb#L112-L118

Capybara::Query は within や find した際の locator を持っているので、 例えば nested within の場合も、全ての within で指定された locator を連結してやればどうにかなるんじゃないかなーってことでこの方法でいきました。 ちなみに「全ての within」については Capybara::Session#scopes を利用しました。

https://github.com/gongo/maizebox/blob/6dd64ac6979329a5fb3abce62113cc5af926bb64/lib/maizebox/renderer.rb#L102-L106

ちなみに、finder では基本 xpath or css で指定するため、混ざっていると単純に連結するだけでは動かないです。 これについては jQuery に加えて jquery-xpath を使っています。

  • xpathjQuery.xpath
  • cssjQuery.find

みたいに切り替えてます。

枠の付け方

これも web-overlay をパク‥‥参考にしてるんですが、「赤枠」「若干透けてる背景色」「指定のノードの幅・高さ・位置」を持つ div を作成し、 $(document.body).append(div) してるだけです。簡単!!

https://github.com/gongo/maizebox/blob/6dd64ac6979329a5fb3abce62113cc5af926bb64/lib/maizebox/renderer.rb#L36-L49

こっからはもう execute_script 無双です。

「そもそも jQueryjquery-xpath をロードしてないサイトの場合どうすんの」という時は、リポジトリに置いてる2つの js を読み込ませてます。

https://github.com/gongo/maizebox/blob/6dd64ac6979329a5fb3abce62113cc5af926bb64/lib/maizebox/js_loader.rb#L11-L17

※ 今気づいたけど p e が残ってる…

Problem

いくつかやること残ってます

  • おそらく within 範囲外(つまりトップレベル) で attention_here するとエラーになるはず
  • js を読み込んでくれない時のエラー処理
    • selenium-webdriver の場合、ブラウザによって読んでくれない理由がまちまち。どれがどれか忘れました
    • Poltergeist だと結構安定して動いてる模様
  • Capybara::Node::Finders#all で取得したやつらを attention_to すると先頭のやつの所にしか枠が付かない
    • 例えば all(:css, 'img.profile') とすると、全ての要素が持つ Capybara::Query はどれも img.profile を返すから。
    • Capybara::Result とか弄くって順番情報持たせればどうにかなるかもしれないけど、まあ考え中

Conclusion

本当の目的は gnawrnip の画像で、どこを注目していいか一目でわかるようにしたいなーって思ってて、 Failed 時の current scope からある程度絞り込めるんじゃないかなーって妄想してるところです。動くといいですね。