How to automate jQueryUI selectmenu with Selenium

2019-08-07 05:52发布

问题:

I'm struggling to get Selenium to select an option from a select menu generated by the jQueryUI official plugin, as described in more detail below, Selenium opens the menu, and then goes for the click to select an item, closes the menu, but the option is not selected.

Some background on the setup:

  • Using Selenium IDE Firefox Plugin
  • Using the jQueryUI SelectMenu Demo page to test on (So we can ensure the code snippet is one everyone can access, and eliminate that the selectmenu is at least properly implemented, as its their demo site)

Test source: http://jqueryui.com/selectmenu/

I have tried targeting the span.ui-selectmenu-text for the selenium click, and then clicking the element with ID=ui-id-5, no luck. I then tried clicking on span.speed-button, and then select the option. Also no luck.

In all cases, the menu opens, and closes, but the original value is still selected.

However, a weird thing happens. In the Selenium IDE, if I do the following, then it IS selected:

  1. Double click on the Command for opening the menu.
  2. Double click on the Command for selecting the correct ID element.
  3. Double click again on the Command for selecting the correct ID element.

Suddenly now, the selected option is updated. But when I run a script end to end, with the two click Commands, it fails again, and just has the default option selected on the page.

The code:

<tr>
    <td>open</td>
    <td>/selectmenu/</td>
    <td></td>
</tr>
<tr>
    <td>click</td>
    <td>css=span.ui-selectmenu-text</td>
    <td></td>
</tr>
<tr>
    <td>click</td>
    <td>id=ui-id-5</td>
    <td></td>
</tr>

If someone could point me in the right direction, any tips or hints, or an actual solution to the problem, that would be great.

I DID search SO before posting this, but the only one I could find was from 3 years ago, and the solution did not work anymore:

how to test using ui selectmenu (jQuery) and selenium

回答1:

If all you're interested in is to select a single value you can do so without clicking and opening the menu. Simply run: |select|locator of the actual SELECT html element|value you want|

This works because jQuery.ui.selectmenu takes the SELECT html element and styles it accordingly.

<tr>
  <td>select</td>
  <td>id=address-state</td>
  <td>label=Alabama</td>
</tr>

Now, if you really want to simulate the whole user input with the UI, then you should use the clickAt command such as:

<tr>
  <td>clickAt</td>
  <td>id=ui-id-1-button</td>
  <td></td>
</tr>

Where ui-id-1-button is the DIV of jQuery.ui.select as inserted on your page (use the inspector in Chrome or FF to see the DOM structure). To select, look in the DOM at the bottom of the page where there is a DIV with an UL normally goes by the generated ID of ui-id-1-menu and inside of it you have the drop down options you can just use a selector and send the clickAt command.

I for one figured out how to scroll through the drop down and take screen shots of options page by page via the user-extensions.js command:

Selenium.prototype.doScreenshootSelectMenuOptions = function(btnLocator, jsonOptions) {
    /**
     * Capture the jQuery.ui.selectmenu dropdown option values page by page screenshots.
     *
     * @param btnLocator    Element locator for the selectmenu button
     * @param jsonOptions   Options for the command as a JSON string. They are <code>
     *                      {"dropdownLocator":"<optional value>", "fileName":"<optional value>", "extension":"<optional value>"}
     *                      <\code>
     *                      <ul>
     *                          <li>dropdownLocator: Optional element locator for hidden selectmenu options locator. If
     *                          ommited it will be computed from options.dropdownLocator</li>
     *                          <li>fileName: Optional name of the file to use for the screenshot. It will be appended
     *                          with ' pg#' where # is the drop down page number. If omitted the default file name is
     *                          'Dropdown'</li>
     *                          <li>extension: Optional name of the file extension to use for the screenshot. If
     *                          omitted the default extension is '.png'</li>
     *                      </ul>
     */
    LOG.debug("user-extensions: doScreenshootSelectMenuOptions('" + btnLocator + "', '" + jsonOptions + "')");

    var btn = this.browserbot.findElement(btnLocator);
    if(btn.id.indexOf('-button') <= 0) {
        throw new SeleniumError("screenshootSelectMenuOptions: wrong value for 'btnLocator'. It's applied to jQuery.ui.selectmenu drop downs!");
    }

    var options = JSON.parse(jsonOptions);
    if(typeof options.dropdownLocator  === 'undefined') {
        options.dropdownLocator = btn.id.substr(0, btn.id.indexOf('-button')) + '-menu';
    } else if(options.dropdownLocator.indexOf('-menu') <= 0) {
        throw new SeleniumError("screenshootSelectMenuOptions: wrong value for 'jsonOptions.dropdownLocator'. It's applied to jQuery.ui.selectmenu drop downs!");
    }

    options.fileName  = (typeof options.fileName  !== 'undefined') ? options.fileName  : "Dropdown";
    options.extension = (typeof options.extension !== 'undefined') ? options.extension : ".png";
    var ddBtn           = this.browserbot.findElement(btnLocator),
        opts            = this.browserbot.findElement(options.dropdownLocator);
    ddBtn.scrollIntoView();     // Scroll to dropdown so it has room to open downwards
    this.doClickAt(btnLocator); // Open dropdown

    var height          = isNaN(parseInt(opts.style.height)) ? opts.scrollHeight : parseInt(opts.style.height),
        scrollHeight    = opts.scrollHeight,
        pages           = scrollHeight / height,
        level           = utils.screenshoot.getNextLevel();
    if(pages <= 0) {
        throw new SeleniumError("screenshootSelectMenuOptions could not computer the number of dropdown menu pages to screenshoot!");
    }

    // For each dropdown values page(s)
    for(var pgNr = 0; pgNr < pages; pgNr++) {
        var pgHeight = (pgNr * height);
        LOG.debug("user-extensions: screenshootSelectMenuOptions opts.scrollTo('0', '" + pgHeight + "')");
        opts.scrollTo(0, pgHeight);
        this.doScreenshotEntirePage(options.fileName + ' pg' + (pgNr + 1) + options.extension, level);
        ddBtn.scrollIntoView();
    }

    this.doClickAt(btnLocator); // Close dropdown
    utils.screenshoot.resetFromLevel(level);
};



回答2:

I actually finally found a really simple answer to this problem. You use mouseOver on the element first, THEN you click it. Problem solved.

Hope it helps.

Paul, as you spent time writing that answer, to be fair to you, I'll leave it up to voters to choose the "accepted answer". Thanks for your contribution.