Capybara の attach_file で、非表示の input 要素にファイルを添付する
TL; DR
- Capybara の finder methods は非表示のものを find できません。その対象は
dispaly: none
になっている要素、だけでなく、opacity: 0
になっている要素も対象になるので注意しましょう。 - 非表示になっている
<input type="file">
要素に対してattach_file
を使う際には、make_visible: true
を渡すことで、非表示になっている要素も一時的に hidden でない状態にしてfind
してくれます。
ストーリー
非表示という落とし穴
Bootswatch という、Bootstrap をカスタマイズしたテーマ集の中から、 Litera というテーマを採用してデザインをあてているアプリケーションがありまして。
そのアプリケーションのとあるページに、CSV ファイルをアップロードするためのこんな感じ↓の HTML がありました。
<div class="form-group"> <div class="input-group mb-3 input-user-file"> <div class="custom-file"> <input type="file" name="csv" id="inputFile" accept="text/csv" required="required" class="custom-file-input"> <label class="custom-file-label" for="inputFile">ファイルを選択</label> </div> </div> </div>
このファイルアップロード機能を確かめるための E2E テストを書きたくて、Capybara を使ってテストを書いておりました。ちなみにわたし Gherkin が大好きです。Turnip が特に好き。
Capybara で <input type="file">
な要素にファイルを読み込ませるためには attach_file
メソッドを使います。ですから
attach_file("inputFile", "/path/to/file")
みたいなコードを書いていたんです...が、なぜかその input
要素が見つからず、以下のエラーが出ました。。
Failure/Error: attach_file("inputFile", Rails.root.join("spec", "fixtures", file_name)) Capybara::ElementNotFound: Unable to find visible file field "inputFile"
rdoc とにらめっこしながら、 attach_file
に渡す locator が間違っているのかなーとか、もしかしたら別の指定の仕方があるのかなーとかいろいろ試したんだけどどれも結果は同じ。
Method: Capybara::Node::Actions#attach_file — Documentation for jnicklas/capybara (master)
save_and_open_page
すると確かに要素は存在しているので、もしやこれは非表示(CSS で display: none
が指定されている)になっている...?と思って確認してみたけど、そんなようすもなく...。。
...と、調べていると気になるキーワードを発見。
ruby - Capybara Unable To Find Input Field - Stack Overflow
Since the login field is invisible (opacity: 0 to allow the emptyText to show through from below) Capybara won't find it by default.
Opacity!それか!?と思って CSS を見てみたところ...果たして。
.custom-file-input { position: relative; z-index: 2; width: 100%; height: calc(2.25rem + 2px); margin: 0; opacity: 0; }
BINGO でした。(ちなみにこれは Bootstrap で定義されている CSS です)
なるほどなあ、確かに透明になってちゃ見えないもんね...。
attach_file には make_visible というオプションが用意されている
Capybara のコードとドキュメントを詳しく見ていきます。
Method: Capybara::Node::Actions#attach_file — Documentation for jnicklas/capybara (master)
In the case of the file field being hidden for styling reasons the
make_visible
option can be used to temporarily change the CSS of the file field, attach the file, and then revert the CSS back to original.
(スタイル上の理由で file フィールドが非表示になっている場合は、 make_visible
オプションを使って一時的に file フィールドの CSS を変更し、ファイルを添付してから CSS を元の状態に戻すことができます。)
なんと。まさに今回のわたしのパターンにおあつらえ向きじゃないですか。「そんなこともあろうかと!」という開発者のかたがたの声が聞こえてきそうです。
GitHub のコードを見てみても、
capybara/actions.rb at 10e12bf0645b010bf50bfa99d0f22b3621a7cd1f · teamcapybara/capybara · GitHub
def attach_file(locator = nil, paths, make_visible: nil, **options) # rubocop:disable Style/OptionalArguments Array(paths).each do |path| raise Capybara::FileNotFound, "cannot attach file, #{path} does not exist" unless File.exist?(path.to_s) end options[:allow_self] = true if locator.nil? # Allow user to update the CSS style of the file input since they are so often hidden on a page if make_visible ff = find(:file_field, locator, options.merge(visible: :all)) while_visible(ff, make_visible) { |el| el.set(paths) } else find(:file_field, locator, options).set(paths) end end
となっていて、 visible: :all
(表示・非表示関係なく、すべての要素を find の対象にする)の状態にしていることがわかります。
おわりに
今回はこのふたつのことを学べました。
- Capybara の finder method は要素の opacity も考慮していること
attach_file
には非表示状態であることを考慮したオプションがあること
結論にたどり着くまでに、ちゃんと save_and_open_page
で要素が表示されているかを確認したことが今回の解決につながるキーだったかな。
成長すると検証のしかたも変わってくるね。
おつかれさまでした!