module WebHelper def self.included(base) base.extend ClassMethods end module ClassMethods # By default, Capybara uses a 30 s wait time, which is more reliable for CI, but too slow # for TDD. Use this to make tests fail fast. Usage: # # describe "foo" do # use_short_wait # ... # end def use_short_wait(seconds = 2) around { |example| Capybara.using_wait_time(seconds) { example.run } } end end def have_input(name, opts = {}) selector = "[name='#{name}']" selector += "[placeholder='#{opts[:placeholder]}']" if opts.key? :placeholder element = page.all(selector).first expect(element.value).to eq(opts[:with]) if element && opts.key?(:with) have_selector selector end def fill_in_fields(field_values) field_values.each do |key, value| begin fill_in key, with: value rescue Capybara::ElementNotFound find_field(key).select(value) end end end def select_by_value(value, options = {}) from = options.delete :from page.find_by_id(from).find("option[value='#{value}']").select_option end def flash_message find('.flash', visible: false).text(:all).strip end def handle_js_confirm(accept = true) page.execute_script "window.confirm = function(msg) { return #{!!accept}; }" yield end def visit_delete(url) response = Capybara.current_session.driver.delete url click_link 'redirected' if response.status == 302 end def set_i18n_locale(locale = 'en') page.execute_script("I18n.locale = '#{locale}'") end def get_i18n_locale page.evaluate_script("I18n.locale;") end def get_i18n_translation(key = nil) page.evaluate_script("I18n.t('#{key}');") end # Fetch the content of a script block # eg. script_content with: 'my-script.com' # Returns nil if not found # Raises an exception if multiple matching blocks are found def script_content(opts = {}) elems = page.all('script', visible: false) elems = elems.to_a.select { |e| e.text(:all).include? opts[:with] } if opts[:with] if elems.none? nil elsif elems.many? raise "Multiple results returned for script_content" else elems.first.text(:all) end end # http://www.elabs.se/blog/53-why-wait_until-was-removed-from-capybara # Do not use this without good reason. Capybara's built-in waiting is very effective. def wait_until(secs = nil) require "timeout" Timeout.timeout(secs || Capybara.default_max_wait_time) do sleep(0.1) until value = yield value end end def wait_until_enabled(selector) wait_until(10) { first("#{selector}:not([disabled='disabled'])") } end def select2_select(value, options) id = options[:from] options[:from] = "#s2id_#{id}" targetted_select2(value, options) end # Support having different texts to search for and to click in the select2 # field. # # This overrides the method in Spree. def targetted_select2_search(value, options) page.execute_script %{$('#{options[:from]}').select2('open')} page.execute_script "$('#{options[:dropdown_css]} input.select2-input').val('#{value}').trigger('keyup-change');" select_select2_result(options[:select_text] || value) end def multi_select2_select(value, options) find("#s2id_#{options[:from]}").find('ul li.select2-search-field').click select_select2_result(value) end def open_select2(selector) page.execute_script "jQuery('#{selector}').select2('open');" end def close_select2(selector) page.execute_script "jQuery('#{selector}').select2('close');" end def select2_search_async(value, options) id = find_label_by_text(options[:from]) options[:from] = "#s2id_#{id}" targetted_select2_search_async(value, options) end def targetted_select2_search_async(value, options) page.execute_script %{$('#{options[:from]}').select2('open')} page.execute_script "$('#{options[:dropdown_css]} input.select2-input').val('#{value}').trigger('keyup-change');" select_select2_result_async(value) end def select_select2_result_async(value) while page.has_selector? "div.select2-searching" return if page.has_selector? "div.select2-no-results" sleep 0.2 end page.execute_script(%{$("div.select2-result-label:contains('#{value}')").mouseup()}) end def accept_js_alert page.driver.browser.switch_to.alert.accept end def angular_http_requests_finished(controller = nil) page.evaluate_script("#{angular_scope(controller)}.injector().get('$http').pendingRequests.length == 0") end def request_monitor_finished(controller = nil) page.evaluate_script("#{angular_scope(controller)}.scope().RequestMonitor.loading == false") end private # Takes an optional angular controller name eg: "LineItemsCtrl", # otherwise finds the first object in the DOM with an angular scope def angular_scope(controller = nil) element = controller ? "[ng-controller=#{controller}]" : '.ng-scope' "angular.element(document.querySelector('#{element}'))" end def wait_for_ajax wait_until { page.evaluate_script("$.active") == 0 } end end