13

I'm working to live life the BDD way. I'm using Cucumber (with Selenium) and happen to be using Twitter Bootstrap modals in my application.

While running Cucumber tests, I was getting a "Selenium::WebDriver::Error::MoveTargetOutOfBoundsError" error. After much searching, debugging and general despair, I have concluded that it has to do with the use of the "fade" parameter in my Bootstrap modals. If I use "fade", the error is thrown:

<div class="modal hide fade" id="info-share-edit-modal" style="display: none;">
  .
  .
  .
</div>

If I remove "fade", then Selenium is full of happiness and my tests clear:

<div class="modal hide" id="info-share-edit-modal" style="display: none;">
  .
  .
  .
</div>

So, I am now removing "fade" from my various modals. But, this makes me sad because I like the fade effect.

Has anyone else experienced problems using Selenium with fade in Bootstrap modals? If so, is there some clever way of getting the two to work nicely together?

By the way (not sure if it matters), I'm Rails 3.2.3, Firefox 13.0.1, and Ubuntu 12.04LTS.

jvillian
  • 19,953
  • 5
  • 31
  • 44
  • I have encountered the same error, using the `reveal.js` from Foundation Zurb and thanks to your suggestion I got it to work by removing the `fade` animation. But I did not find a real solution. So interested to hear how others solved this. – nathanvda Sep 12 '12 at 22:20
  • When exactly does the error occur? – spinningarrow Oct 15 '12 at 10:56

7 Answers7

8

I did a quick test with inserting a WebDriverWait that takes a look at the opacity of the modal. It seems to work, but time will tell as (at least for me) it's an intermittent problem. Here's my implementation in Java.

//Ensure the modal is done animating
new WebDriverWait(driver, 5).until(
    new ExpectedCondition<Boolean>() {
        @Override
        public Boolean apply(WebDriver webDriver) {         
            return webDriver.findElement(By.id("videoModal")).getCssValue("opacity").equals("1");
        }
    }
);
user1965252
  • 81
  • 1
  • 3
  • This [ticket](https://github.com/seleniumhq/selenium-google-code-issue-archive/issues/2766) for the Chrome driver suggest that you have it right. In so many words, it says that an element has to stop animating before you can click on it. – jpaugh Jun 17 '16 at 14:50
2

I solved it this way (using c#). It is fast and hasn't failed once.

public static void WaitForModal(this RemoteWebDriver driver)
{
    using (driver.NoImplicitWait())
    {
        var wait = new WebDriverWait(driver, TimeSpan.FromSeconds(30));
        wait.Until(d => d.FindElements(By.ClassName("modal-backdrop").Count == 0);
    }
}

NoImplicitWait is used to temporarily disable the driver implicit wait.

public static NoImplicitWait NoImplicitWait(this IWebDriver driver)
{
    return new NoImplicitWait(driver);
}

public sealed class NoImplicitWait : IDisposable
{
    private readonly IWebDriver _driver;

    public NoImplicitWait(IWebDriver driver)
    {
        _driver = driver;
        _driver.Manage().Timeouts().ImplicitlyWait(TimeSpan.FromSeconds(0));
    }

    public void Dispose()
    {
        _driver.Manage().Timeouts().ImplicitlyWait(TimeSpan.FromSeconds(30));
    }
}
magnusarinell
  • 1,127
  • 14
  • 22
0

Put in a flag so that in the test environment it doesn't fade, but it does in every other environment.

nicholaides
  • 19,211
  • 12
  • 66
  • 82
0

c# code

I had the same problem and this code is working for me since 2+ months, no more crash.

 public static void WaitForModal(this IWebDriver driver)
    {
        wait.Until<IWebDriver>((d) =>
        {
            if (driver.FindElements(By.ClassName("modal-backdrop")).Count == 0)
            {
                return driver;
            }
            return null;
        });
    }

It waits until it finds no more IWebElement that have a class of "modal-backdrop".

Happy Bird
  • 1,012
  • 2
  • 11
  • 29
0

Improving on user1965252's answer, this worked for me. Just replace the-modal-id with your modal div id.

new WebDriverWait(driver, TIME_OUT_IN_SECONDS).until(and(
        new ExpectedCondition<Boolean>() {
           @Override
           public Boolean apply(WebDriver webDriver) {
               return webDriver.findElement(id("the-modal-id"))
                       .getCssValue("opacity").equals("0");
           }
        },
        numberOfElementsToBe(cssSelector("div.modal-backdrop"), 0)
));
Wojtek
  • 1,410
  • 2
  • 16
  • 31
0

What I generally do is assert against some content that should be visible on the modal (or not visible when it is fading out):

expect(page).to have_content('My Modal Header')
expect(page).to have_no_content('My Modal Header')

It's important to use .to have_no_content and not .not_to have_content, as have_no_content will wait for a period for the thing to be true.

In a pinch, you can also check for modal CSS selectors. Bootstrap adds an in class when the modal is visible:

expect(page).to have_selector('.modal.in')
expect(page).to have_no_selector('.modal.in')
lobati
  • 9,284
  • 5
  • 40
  • 61
-1

In a selenium test case when application opens the bootstrap modal, add a pause command to ask selenium to pause for one second before interacting with content of your modal:

Command: pause /
Target: 1000 /
Value: (leave empty)
John Dvorak
  • 26,799
  • 13
  • 69
  • 83
ramin
  • 157
  • 15