Mink icon indicating copy to clipboard operation
Mink copied to clipboard

How do deselect a particular SELECT option in a multi-select

Open aik099 opened this issue 12 years ago • 5 comments

In Selenium individual options can be easily selected/deselect by clicking on them (even if SELECT itself isn't expanded). It even doesn't force you to do a Ctrl/CMD+Click on an option to append it to current selection. I haven't seen such luxury in other JavaScript enabled drivers (e.g. Sahi, Zombie), where the only method that allows to manipulate SELECT element is called setSelected (or similar) and accepts an array of option values to be selected.

Without individual option deselection the only possible way is to call NodeElement::selectOption method multiple times in following fashion:

$multi_select->selectOption('value1', true); // true - to reset selection to this option
$multi_select->selectOption('value2', false); // false - to append to current selection
$multi_select->selectOption('valueN', false); // false - to append to current selection
...

Having some sort of resetSelection method of a driver, that would uncheck all options would be a great help in this case. Implementing such method however isn't that easy as it sounds, because native setSelected methods of underlying browser controllers:

  1. doesn't support selecting non-existing options just to unselect all of them
  2. doesn't support setValue('') call on a multi-select just to unselect all options

Without changing DriverInterface and associated drivers I don't have any other choice for deselection an option, then:

  1. use getValue to get currently selected options
  2. remove options, that needs to be deselected from resulting array
  3. call selectOption multiple times (see example in the beginning)

This approach has major drawback - multiple onchange event calls, that doesn't really happen when user himself is changing multi-select selection.

Any ideas would be appreciated.

aik099 avatar Aug 20 '13 17:08 aik099

Also deselecting last selected option might be bad idea, because it's impossible to call selectOption with a value that doesn't exist in a SELECT just to unselect all other options.

aik099 avatar Aug 20 '13 18:08 aik099

:+1: Just had the same issue, agree that only with a change of the driver interface and the drivers this issue could be solved. Would be nice if the driver were to offer some methods to manipulate the DOM/single nodes (add/remove/edit attributes)

cgrossde avatar Mar 19 '15 13:03 cgrossde

@cgrossde Mink is meant to emulate the browser. Adding or removing attributes on DOM nodes is not something a browser let you do (the DevTools let you do that, not the interface of a normal browser user). So I'm totally against adding such method in our DriverInterface (thus, it might make things hard for BrowserKit's form handling for instance).

Btw, to deselect all options of a multi-select, ->setValue(array()) is supported now (it was maybe not in August 2013). Multi-select value is an array. @aik099 using setValue() with the new selected options works now, which solves the issue of multiple change events. The issue with your resetSelection is that it does not play well with single-select, where something is always selected

stof avatar Mar 19 '15 13:03 stof

Thanks for the fast reply, I just tested it an this works for me:

UPDATE: Incorporated @stof 's hint from bellow

public function deselectOption($select, $option) {
    $field = $this->getSession()->getPage()->findField($select);
    if (null === $field) {
        throw Behat\Mink\Exception('form field (select) :"'.$select.'" not found');
    }

    $values = $field->getValue();
    if($values !== null && is_array($values)) {
        // Array -> Multiselect -> check all values
        for($i = 0; $i < count($values); $i++) {
            // Remove our option from $values
            if($values[$i] === $option) {
                array_splice($values, $i, 1);
                break;
            }
        }
        $field->setValue($values);
    }
}

cgrossde avatar Mar 19 '15 14:03 cgrossde

@cgrossde your own code is triggering multiple change events. You should use $field->setValue($values) as the driver is then smart enough to trigger a single change event (or at least should be if it want to have passing tests)

stof avatar Mar 19 '15 14:03 stof