126

I am trying to select an option from a drop down for the angular e2e tests using protractor.

Here is the code snippet of the select option:

<select id="locregion" class="create_select ng-pristine ng-invalid ng-invalid-required" required="" ng-disabled="organization.id !== undefined" ng-options="o.id as o.name for o in organizations" ng-model="organization.parent_id">
    <option value="?" selected="selected"></option>
    <option value="0">Ranjans Mobile Testing</option>
    <option value="1">BeaverBox Testing</option>
    <option value="2">BadgerBox</option>
    <option value="3">CritterCase</option>
    <option value="4">BoxLox</option>
    <option value="5">BooBoBum</option>
</select>

I have tried:

ptor.findElement(protractor.By.css('select option:1')).click();

This gives me the following error:

An invalid or illegal string was specified Build info: version: '2.35.0', revision: 'c916b9d', time: '2013-08-12 15:42:01' System info: os.name: 'Mac OS X', os.arch: 'x86_64', os.version: '10.9', java.version: '1.6.0_65' Driver info: driver.version: unknown

I have also tried:

ptor.findElement(protractor.By.xpath('/html/body/div[2]/div/div[4]/div/div/div/div[3]/ng-include/div/div[2]/div/div/organization-form/form/div[2]/select/option[3]')).click();

This gives me the following error:

ElementNotVisibleError: Element is not currently visible and so may not be interacted with Command duration or timeout: 9 milliseconds Build info: version: '2.35.0', revision: 'c916b9d', time: '2013-08-12 15:42:01' System info: os.name: 'Mac OS X', os.arch: 'x86_64', os.version: '10.9', java.version: '1.6.0_65' Session ID: bdeb8088-d8ad-0f49-aad9-82201c45c63f Driver info: org.openqa.selenium.firefox.FirefoxDriver Capabilities [{platform=MAC, acceptSslCerts=true, javascriptEnabled=true, browserName=firefox, rotatable=false, locationContextEnabled=true, version=24.0, cssSelectorsEnabled=true, databaseEnabled=true, handlesAlerts=true, browserConnectionEnabled=true, nativeEvents=false, webStorageEnabled=true, applicationCacheEnabled=false, takesScreenshot=true}]

Can anyone please help me with this problem or throw some light on what i might be doing wrong here.

alecxe
  • 462,703
  • 120
  • 1,088
  • 1,195
Ranjan Bhambroo
  • 1,371
  • 2
  • 9
  • 6

34 Answers34

258

For me worked like a charm

element(by.cssContainingText('option', 'BeaverBox Testing')).click();
Christopher Moore
  • 15,626
  • 10
  • 42
  • 52
Fatz
  • 2,589
  • 2
  • 11
  • 2
  • 2
    Minor note - v0.22 only (I just went to replace my code with this today, and had to upgrade to get it) – PaulL Jul 25 '14 at 21:47
  • Is there a way for targeting elements using this? Such as having duplicate State select menus. – Christopher Marshall Sep 15 '14 at 21:41
  • 12
    Christopher, `ElementFinder` is chain-able, so you can do: `element(by.css('.specific-select')).element(by.cssContainingText('option', 'BeaverBox Testing')).click();` – Joel Kornbluh Sep 29 '14 at 21:10
  • 7
    Note that you can get partial matches, so 'Small' will match 'Extra Small'. – TrueWill Aug 18 '15 at 16:12
  • 3
    To add to TrueWill's comment: The down side of this solution is that if you have two similar options it will use the last found option - which threw up errors based on the wrong selection. What worked for me was http://stackoverflow.com/a/25333326/1945990 – user115014 Sep 09 '15 at 08:21
  • Using this method the value of `form.$pristine` was not updated . Using the `sendKeys()` way worked ok for me. – David Casillas Mar 05 '17 at 10:51
  • If a partial match is a problem, you can use a RegExp instead of a string. – twm Sep 12 '18 at 18:44
90

I had a similar problem, and eventually wrote a helper function that selects dropdown values.

I eventually decided that I was fine selecting by option number, and therefore wrote a method that takes an element and the optionNumber, and selects that optionNumber. If the optionNumber is null it selects nothing (leaving the dropdown unselected).

var selectDropdownbyNum = function ( element, optionNum ) {
  if (optionNum){
    var options = element.all(by.tagName('option'))   
      .then(function(options){
        options[optionNum].click();
      });
  }
};

I wrote a blog post if you want more detail, it also covers verifying the text of the selected option in a dropdown: http://technpol.wordpress.com/2013/12/01/protractor-and-dropdowns-validation/

Sergey Pleshakov
  • 7,964
  • 2
  • 17
  • 40
PaulL
  • 6,650
  • 3
  • 35
  • 39
  • 1
    Didn't work for me, got element.findElements is not a function. – Justin Mar 19 '18 at 15:52
  • even if it works, there are multiple problems with this code. 1. It's not clear why variable `options` is created if the output of the command is always `undefined`. 2. For getting one element out of elementArrayFinder, it's better (and easier) to use .get() method. 3. If you pass an element to the function, then `element.all` statement won't work. 4. It's better to use `async` functions these days. Here is an example https://stackoverflow.com/a/66110526/9150146 – Sergey Pleshakov Apr 01 '21 at 18:49
32

An elegant approach would involve making an abstraction similar to what other selenium language bindings offer out-of-the-box (e.g. Select class in Python or Java).

Let's make a convenient wrapper and hide implementation details inside:

var SelectWrapper = function(selector) {
    this.webElement = element(selector);
};
SelectWrapper.prototype.getOptions = function() {
    return this.webElement.all(by.tagName('option'));
};
SelectWrapper.prototype.getSelectedOptions = function() {
    return this.webElement.all(by.css('option[selected="selected"]'));
};
SelectWrapper.prototype.selectByValue = function(value) {
    return this.webElement.all(by.css('option[value="' + value + '"]')).click();
};
SelectWrapper.prototype.selectByPartialText = function(text) {
    return this.webElement.all(by.cssContainingText('option', text)).click();   
};
SelectWrapper.prototype.selectByText = function(text) {
    return this.webElement.all(by.xpath('option[.="' + text + '"]')).click();   
};

module.exports = SelectWrapper;

Usage example (note how readable and easy-to-use it is):

var SelectWrapper  = require('select-wrapper');
var mySelect = new SelectWrapper(by.id('locregion'));

# select an option by value
mySelect.selectByValue('4');

# select by visible text
mySelect.selectByText('BoxLox');

Solution taken from the following topic: Select -> option abstraction.


FYI, created a feature request: Select -> option abstraction.

Community
  • 1
  • 1
alecxe
  • 462,703
  • 120
  • 1,088
  • 1,195
22
element(by.model('parent_id')).sendKeys('BKN01');
Martin Geisler
  • 72,968
  • 25
  • 171
  • 229
Sandesh Danwale
  • 237
  • 2
  • 2
15

To access a specific option you need to provide the nth-child() selector:

ptor.findElement(protractor.By.css('select option:nth-child(1)')).click();
bekite
  • 3,444
  • 2
  • 23
  • 31
  • This does not provide an answer to the question. To critique or request clarification from an author, leave a comment below their post. – jpw Oct 25 '13 at 22:33
  • @jpw is my answer wrong. or is it simply the formulation of my answer thats wrong? – bekite Oct 25 '13 at 22:37
  • The formulation of an answer as a question is why. I don't know if it's the correct answer. If it is you should formulate it as such. – jpw Oct 25 '13 at 22:41
  • 3
    @bekite Didn't work for me.Still getting the error Element Not visible error when i tried your suggestion – Ranjan Bhambroo Oct 26 '13 at 01:43
8

This is how i did my selection.

function switchType(typeName) {
     $('.dropdown').element(By.cssContainingText('option', typeName)).click();
};
Xotabu4
  • 3,063
  • 17
  • 29
5

Here's how I did it:

$('select').click();
$('select option=["' + optionInputFromFunction + '"]').click();
// This looks useless but it slows down the click event
// long enough to register a change in Angular.
browser.actions().mouseDown().mouseUp().perform();
yurisich
  • 6,991
  • 7
  • 42
  • 63
5

Try this, it is working for me:

element(by.model('formModel.client'))
    .all(by.tagName('option'))
    .get(120)
    .click();
Dmitri Zaitsev
  • 13,548
  • 11
  • 76
  • 110
Javeed
  • 59
  • 1
  • 1
4

You can try this hope it will work

element.all(by.id('locregion')).then(function(selectItem) {
  expect(selectItem[0].getText()).toEqual('Ranjans Mobile Testing')
  selectItem[0].click(); //will click on first item
  selectItem[3].click(); //will click on fourth item
});
glepretre
  • 8,154
  • 5
  • 43
  • 57
Zahid Afaque
  • 243
  • 4
  • 13
4

Another way to set an option element:

var select = element(by.model('organization.parent_id'));
select.$('[value="1"]').click();
  • var orderTest = element(by.model('ctrl.supplier.orderTest')) orderTest.$('[value="1"]').click(); am getting error By(css selector, [value="3"]) – Anuj K.C. Oct 20 '16 at 09:12
3

To select items (options) with unique ids like in here:

<select
    ng-model="foo" 
    ng-options="bar as bar.title for bar in bars track by bar.id">
</select>

I'm using this:

element(by.css('[value="' + neededBarId+ '"]')).click();
Vladius
  • 4,268
  • 2
  • 20
  • 21
3

We wrote a library which includes 3 ways to select an option:

selectOption(option: ElementFinder |Locator | string, timeout?: number): Promise<void>

selectOptionByIndex(select: ElementFinder | Locator | string, index: number, timeout?: number): Promise<void>

selectOptionByText(select: ElementFinder | Locator | string, text: string, timeout?: number): Promise<void>

Additional feature of this functions is that they wait for the element to be displayed before any action on the select is performed.

You can find it on npm @hetznercloud/protractor-test-helper. Typings for TypeScript are provided as well.

crashbus
  • 1,678
  • 18
  • 37
2

Maybe not super elegant, but efficient:

function selectOption(modelSelector, index) {
    for (var i=0; i<index; i++){
        element(by.model(modelSelector)).sendKeys("\uE015");
    }
}

This just sends key down on the select you want, in our case, we are using modelSelector but obviously you can use any other selector.

Then in my page object model:

selectMyOption: function (optionNum) {
       selectOption('myOption', optionNum)
}

And from the test:

myPage.selectMyOption(1);
Calahad
  • 1,408
  • 14
  • 22
2

The problem is that solutions that work on regular angular select boxes do not work with Angular Material md-select and md-option using protractor. This one was posted by another, but it worked for me and I am unable to comment on his post yet (only 23 rep points). Also, I cleaned it up a bit, instead of browser.sleep, I used browser.waitForAngular();

element.all(by.css('md-select')).each(function (eachElement, index) {
    eachElement.click();                    // select the <select>
    browser.waitForAngular();              // wait for the renderings to take effect
    element(by.css('md-option')).click();   // select the first md-option
    browser.waitForAngular();              // wait for the renderings to take effect
});
  • I just use this: `element(by.model('selectModel')).click(); element(by.css('md-option[value="searchOptionValue"]')).click(); ` – Luan Nguyen Jun 10 '16 at 17:55
  • Here's another approach: `mdSelectElement.getAttribute('aria-owns').then( function(val) { let selectMenuContainer = element(by.id(val)); selectMenuContainer.element(by.css('md-option[value="foo"]')).click(); } );` – gbruins Oct 18 '16 at 01:05
1

There's an issue with selecting options in Firefox that Droogans's hack fixes that I want to mention here explicitly, hoping it might save someone some trouble: https://github.com/angular/protractor/issues/480.

Even if your tests are passing locally with Firefox, you might find that they're failing on CircleCI or TravisCI or whatever you're using for CI&deployment. Being aware of this problem from the beginning would have saved me a lot of time:)

Community
  • 1
  • 1
Julia Jacobs
  • 469
  • 4
  • 7
1

I've been trawling the net for an answer on how to select an option in a model dropdown and i've used this combination which has helped me out with Angular material.

element(by.model("ModelName")).click().element(By.xpath('xpathlocation')).click();

it appears that when throwing the code all in one line it could find the element in the dropdown.

Took a lot of time for this solution I hope that this helps someone out.

tony2tones
  • 1,422
  • 1
  • 18
  • 19
1

Helper to set the an option element:

selectDropDownByText:function(optionValue) {
            element(by.cssContainingText('option', optionValue)).click(); //optionValue: dropDownOption
        }
Adnan Ghaffar
  • 1,345
  • 7
  • 26
  • 46
1

If below is the given dropdown-

            <select ng-model="operator">
            <option value="name">Addition</option>
            <option value="age">Division</option>
            </select>

Then protractorjs code can be-

        var operators=element(by.model('operator'));
      operators.$('[value=Addition]').click();

Source-https://github.com/angular/protractor/issues/600

Ajay Patil
  • 159
  • 1
  • 7
1

Select option by Index:

var selectDropdownElement= element(by.id('select-dropdown'));
selectDropdownElement.all(by.tagName('option'))
      .then(function (options) {
          options[0].click();
      });
Shardul
  • 984
  • 1
  • 12
  • 19
1

I've improved a bit the solution written by PaulL. First of all I fixed the code to be compatible with the last Protractor API. And then I declare the function in 'onPrepare' section of a Protractor config file as a member of the browser instance, so it can be referenced form any e2e spec.

  onPrepare: function() {
    browser._selectDropdownbyNum = function (element, optionNum) {
      /* A helper function to select in a dropdown control an option
      * with specified number.
      */
      return element.all(by.tagName('option')).then(
        function(options) {
          options[optionNum].click();
        });
    };
  },
trunikov
  • 97
  • 1
  • 4
1

The below example is the easiest way . I have tested and passed in Protractor Version 5.4.2

//Drop down selection  using option's visibility text 

 element(by.model('currency')).element(by.css("[value='Dollar']")).click();
 Or use this, it   $ isshort form for  .By.css
  element(by.model('currency')).$('[value="Dollar"]').click();

//To select using index

var select = element(by.id('userSelect'));
select.$('[value="1"]').click(); // To select using the index .$ means a shortcut to .By.css

Full code

describe('Protractor Demo App', function() {

  it('should have a title', function() {

     browser.driver.get('http://www.way2automation.com/angularjs-protractor/banking/#/');
    expect(browser.getTitle()).toEqual('Protractor practice website - Banking App');
    element(by.buttonText('Bank Manager Login')).click();
    element(by.buttonText('Open Account')).click();

    //Drop down selection  using option's visibility text 
  element(by.model('currency')).element(by.css("[value='Dollar']")).click();

    //This is a short form. $ in short form for  .By.css
    // element(by.model('currency')).$('[value="Dollar"]').click();

    //To select using index
    var select = element(by.id('userSelect'));
    select.$('[value="1"]').click(); // To select using the index .$ means a shortcut to .By.css
    element(by.buttonText("Process")).click();
    browser.sleep(7500);// wait in miliseconds
    browser.switchTo().alert().accept();

  });
});
user158
  • 12,852
  • 7
  • 62
  • 94
Sameera De Silva
  • 1,722
  • 1
  • 22
  • 41
1

If none of the answer's above worked for you, try this

works with async/await too

For selecting options by text

let textOption = "option2"
await element(by.whichever('YOUR_DROPDOWN_SELECTOR'))
  .getWebElement()
  .findElement(by.xpath(`.//option[text()="${textOption}"]`))
  .click();

or by number

let optionNumber = 2
await element(by.whichever('YOUR_DROPDOWN_SELECTOR'))
  .getWebElement()
  .findElement(by.xpath(`.//option[${optionNumber}]`))
  .click();

Of course you may need to modify the xpath of child options

Don't ask me why, but this is the only way I could automate my dropdowns, when I lost hope already


Update

There was actually one case when even this approach didnt work. THe work around was a bit ugly but worked. I simply had to select the value two times

Sergey Pleshakov
  • 7,964
  • 2
  • 17
  • 40
0

We wanted to use the elegant solution up there using angularjs material but it didnt work because there are actually no option / md-option tags in the DOM until the md-select has been clicked. So the "elegant" way didn't work for us (note angular material!) Here is what we did for it instead, don't know if its the best way but its definately working now

element.all(by.css('md-select')).each(function (eachElement, index) {
    eachElement.click();                    // select the <select>
    browser.driver.sleep(500);              // wait for the renderings to take effect
    element(by.css('md-option')).click();   // select the first md-option
    browser.driver.sleep(500);              // wait for the renderings to take effect
});

We needed to have 4 selects selected and while the select is open, there is an overlay in the way of selecting the next select. thats why we need to wait 500ms to make sure we don't get into trouble with the material effects still being in action.

Dmitri Zaitsev
  • 13,548
  • 11
  • 76
  • 110
pascalwhoop
  • 2,984
  • 3
  • 26
  • 40
0

Another way to set an option element:

var setOption = function(optionToSelect) {

    var select = element(by.id('locregion'));
    select.click();
    select.all(by.tagName('option')).filter(function(elem, index) {
        return elem.getText().then(function(text) {
            return text === optionToSelect;
        });
    }).then(function(filteredElements){
        filteredElements[0].click();
    });
};

// using the function
setOption('BeaverBox Testing');
flaviomeira10
  • 566
  • 11
  • 24
0
----------
element.all(by.id('locregion')).then(function(Item)
{
 // Item[x] = > // x is [0,1,2,3]element you want to click
  Item[0].click(); //first item

  Item[3].click();     // fourth item
  expect(Item[0].getText()).toEqual('Ranjans Mobile Testing')


});
Pramod Dutta
  • 111
  • 2
  • 12
0

You can select dropdown options by value: $('#locregion').$('[value="1"]').click();

Sander Lepik
  • 65
  • 1
  • 9
0

Here is how to do it by either option value or index. This example is a bit crude, but it shows how to do what you want:

html:

<mat-form-field id="your-id">
    <mat-select>
        <mat-option [value]="1">1</mat-option>
        <mat-option [value]="2">2</mat-option>
    </mat-select>
</mat-form-field>

ts:

function selectOptionByOptionValue(selectFormFieldElementId, valueToFind) {

  const formField = element(by.id(selectFormFieldElementId));
  formField.click().then(() => {

    formField.element(by.tagName('mat-select'))
      .getAttribute('aria-owns').then((optionIdsString: string) => {
        const optionIds = optionIdsString.split(' ');    

        for (let optionId of optionIds) {
          const option = element(by.id(optionId));
          option.getText().then((text) => {
            if (text === valueToFind) {
              option.click();
            }
          });
        }
      });
  });
}

function selectOptionByOptionIndex(selectFormFieldElementId, index) {

  const formField = element(by.id(selectFormFieldElementId));
  formField.click().then(() => {

    formField.element(by.tagName('mat-select'))
      .getAttribute('aria-owns').then((optionIdsString: string) => {
        const optionIds = optionIdsString.split(' ');

        const optionId = optionIds[index];
        const option = element(by.id(optionId));
        option.click();
      });
  });
}

selectOptionByOptionValue('your-id', '1'); //selects first option
selectOptionByOptionIndex('your-id', 1); //selects second option
KVarmark
  • 226
  • 3
  • 4
0
static selectDropdownValue(dropDownLocator,dropDownListLocator,dropDownValue){
    let ListVal ='';
    WebLibraryUtils.getElement('xpath',dropDownLocator).click()
      WebLibraryUtils.getElements('xpath',dropDownListLocator).then(function(selectItem){
        if(selectItem.length>0)
        {
            for( let i =0;i<=selectItem.length;i++)
               {
                   if(selectItem[i]==dropDownValue)
                   {
                       console.log(selectItem[i])
                       selectItem[i].click();
                   }
               }            
        }

    })

}
Javed Ali
  • 41
  • 1
  • 8
0

We can create a custom DropDown class for this and add a method as:

async selectSingleValue(value: string) {
        await this.element.element(by.xpath('.//option[normalize-space(.)=\'' + value + '\']')).click();
    }

Also, to verify what value is currently selected, we can have:

async getSelectedValues() {
        return await this.element.$('option:checked').getText();
    }
0

This is a simple one line answer in which angular has special locator which can help to select and index from list.

element.all(by.options('o.id as o.name for o in organizations')).get(Index).click()
David Buck
  • 3,752
  • 35
  • 31
  • 35
  • While this code may solve the question, [including an explanation](//meta.stackexchange.com/q/114762) of how and why this solves the problem would really help to improve the quality of your post, and probably result in more up-votes. Remember that you are answering the question for readers in the future, not just the person asking now. Please [edit] your answer to add explanations and give an indication of what limitations and assumptions apply. [From Review](/review/late-answers/26301327) – double-beep Jun 02 '20 at 16:36
0

This worked fine for Angular Material

element(by.css("mat-select[formcontrolname=myMatSelect]")).sendKeys(valueToSet);

It should not set the "valueToSet" if it's not present in your options.

Saad Joudi
  • 595
  • 4
  • 5
0

The simplest method to select an option using the index I have found is,

var selectDropdownElement = await element(by.xpath("Some Xpath"));
                await selectDropdownElement.all(by.tagName('option')).then(function (options) {
                        options[index].click();
                });

'index' is the option number which you want to click

sarath
  • 1
  • 1
-1

Select the option by CSS property

element(by.model("organization.parent_id")).element(by.css("[value='1']")).click();

or

element(by.css("#locregion")).element(by.css("[value='1']")).click();

Where locregion(id), organization.parent_id(model name) are the attributes of select element.

Kanhaiya Kumar
  • 131
  • 1
  • 6
-1

You can select drop down options like this:

element(by.xpath(
'//*[@id="locregion"]//option[contains(text(),"Ranjans Mobile Testing")]'
)).click();
Xotabu4
  • 3,063
  • 17
  • 29