Skip to content
This repository

Wait until enabled button is present #771

Closed
badeball opened this Issue · 15 comments

8 participants

Jonas Amundsen Jonas Nicklas Jo Liss Peter Philips Erik Ostrom Eric Pierce leevyzz Adam Waite
Jonas Amundsen

Hi,

First of all--let me thank you for a wonderful piece of software!

I am currently creating automated integration tests for an ExtJS application, which rely heavily on Javascript and Ajax. (Maybe that's obvious.) I'm 100% dependant on the wait time defined by Capybara.default_wait_time, as elements may not be present at once, due to Ajax calls not being finished.

However, now I've encountered a similar problem, for which there does not seem to be an elegant solution to. The problem is that the button is present, but it is not enabled at once. So, I need something that will help me select a button from a locator string that is also enabled. I ended up monkey patching Capybara like this.

module Capybara
  class Session
    def click_enabled_button(locator)
      current_node.click_enabled_button(locator)
    end
  end

  module Node
    module Actions
      def click_enabled_button(locator)
        msg = "no enabled button with value or id or text '#{locator}' found"
        find(:xpath, XPath::HTML.enabled_button(locator), :message => msg).click
      end
    end
  end
end

module XPath
  module HTML
    def enabled_button(locator)
      button(locator)[~attr(:disabled)]
    end
  end
end

Do you think this is the right way to do that? Is that something that may be implemented in Capybara? (If so, I guess I could make a pull request.)

Jonas Nicklas
Owner

It does seem to make sense that we only ever want to click enabled buttons, to be honest. I don't think we should have a separate click_enabled_button action though. I'm torn about this. @joliss, what do you think?

Jo Liss
Collaborator

Seems reasonable to me to change the default behavior of click_button/click_on to exclude disabled buttons.

Jonas Nicklas
Owner

How about something like fill_in then? If we disallow it in one place, we should disallow it everywhere, right?

Jonas Amundsen

Good point. The problem applies to anything that can be disabled, really.

Jo Liss
Collaborator

Yes, looks like the XPaths for all form interaction methods would have to be updated.

Jonas Amundsen

@synth, I'm not sure if your problem is related to this. I also have stumbled upon issues where Capybara has simulated user actions before dynamic javascripts has been loaded. I solved it by extending my step definition which navigates the browser.

Given /^(?:|I )am on (.+)$/ do |page_name|
  page_url = $config[:host] + path_to page_name

  unless browser.current_url.include? page_url
    browser.visit page_url
    browser.wait_until do
      browser.evaluate_script "Ext.isReady"
    end
  end
end

You get the gist, right? I'm sure there's something equivalent in RequireJS.

Peter Philips

@badeball you're a genius! this has solved the remaining funky issues I was having! I created a helper to handle the various ui cases that I needed to wait for:

https://gist.github.com/3292441

Jo Liss
Collaborator

@synth Re your gist, you can implement wait_until_overlay_has_popped_up without resorting to evaluate_script by doing something like this (assuming RSpec; untested):

page.should have_css('.div.overlay', visible: true)

It will wait for the element to appear. For the other method, analogously with have_no_css.

Erik Ostrom eostrom referenced this issue in wapcaplet/kelp
Merged

Capybara 2 #4

Erik Ostrom

I agree that you never want to press a disabled button, but it can be useful to find one — you may actually want to verify that a button is present but not enabled.

I don't personally have a use case for this right now, I just ran across it while updating Kelp. I'm just bringing it up for (belated) discussion.

Eric Pierce

From Kelp's perspective, the problem of disabled-button invisibility appears to be due to this xpath commit made at around the same time as the above fix; I'll open an issue over there with the details. (Edit: see xpath issue #47)

Regarding a use case for this, I would put forward the example where a "Save" button is dynamically enabled or disabled depending on user actions within a form. For instance, a Cucumber scenario might say:

  When I leave a required field blank
  Then the "Save" button should be disabled
  When I fill in all required fields
  Then the "Save" button should be enabled

I agree with the above, that it doesn't make much sense to click a disabled button, but even if a button (or other form element) is disabled, it still exists, and is visible.

leevyzz

I am a newer. I want to verify if a button is disabled, how to do it?

Adam Waite

How can we do this now that wait_until has been removed? My JS is loaded asynchronously with require and Capybara gets there before the assets are loaded... (I can use sleep but that's lame)

Jonas Amundsen

@adamwaite, given that Capybara is simply a fast user, one can assume that a regular user with a slow connection might experience similar troubles when actuating on your site before assets has finished loading asynchronously. How would you solve that? You could for instance output buttons in a disabled state and enable them with Javascript, thus forcing a user to wait until it has loaded. Or you can query some other element that you know will change once that the Javascript has loaded.

Adam Waite

ah yes, good thinking. That's given me some new ideas to try out tomorrow. Thanks.

Adam Waite

My solution, in case any stumbles upon a similar problem...

Append class ready to an element in the DOM using an asynchronous module that should come last. Then query that element. I hade a helper in support/some_helper_file.rb:

def js_ready_page
  page.find ".ready"
end

and then we can:

let(:user_menu_button_click) { -> { js_ready_page.find('#present-user-menu-button').trigger 'click' }  }
    context 'selecting the user menu' do
      before { user_menu_button_click.call }
      context 'unauthenticated user' do
        specify { js_ready_page.should have_css "#user-menu-overlay.presented[data-authenticated~='false']" }
      end
      context 'unauthenticated user' do
        specify { js_ready_page.should have_css "#user-menu-overlay.presented[data-authenticated~='true']" }
      end
    end

Inspired by @badeball, Thanks.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Something went wrong with that request. Please try again.