Comment sélectionner une option dans les tests de protractorjs e2e déroulants


121

J'essaie de sélectionner une option dans une liste déroulante pour les tests angulaires e2e à l'aide d'un rapporteur.

Voici l'extrait de code de l'option de sélection:

<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>

J'ai essayé:

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

Cela me donne l'erreur suivante:

Une chaîne non valide ou illégale a été spécifiée Informations de construction: version: '2.35.0', révision: 'c916b9d', heure: '2013-08-12 15:42:01' Informations système: os.name: 'Mac OS X' , os.arch: 'x86_64', os.version: '10 .9 ', java.version:' 1.6.0_65 'Informations sur le pilote: driver.version: unknown

J'ai également essayé:

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();

Cela me donne l'erreur suivante:

ElementNotVisibleError: L'élément n'est actuellement pas visible et ne peut donc pas interagir avec la durée ou le délai d'expiration de la commande: 9 millisecondes Informations de construction: version: '2.35.0', révision: 'c916b9d', heure: '2013-08-12 15:42: 01 'Informations système: os.name:' Mac OS X ', os.arch:' x86_64 ', os.version: '10 .9', java.version: '1.6.0_65' ID de session: bdeb8088-d8ad-0f49-aad9 -82201c45c63f Informations sur le pilote: 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}]

Quelqu'un peut-il s'il vous plaît m'aider avec ce problème ou jeter un peu de lumière sur ce que je pourrais faire de mal ici.

Réponses:


88

J'ai eu un problème similaire et j'ai finalement écrit une fonction d'aide qui sélectionne les valeurs de liste déroulante.

J'ai finalement décidé que j'allais bien sélectionner par numéro d'option, et j'ai donc écrit une méthode qui prend un élément et l'optionNumber, et sélectionne cet optionNumber. Si l'optionNumber est null, il ne sélectionne rien (en laissant la liste déroulante non sélectionnée).

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

J'ai écrit un article de blog si vous voulez plus de détails, il couvre également la vérification du texte de l'option sélectionnée dans une liste déroulante: http://technpol.wordpress.com/2013/12/01/protractor-and-dropdowns-validation/


1
Cela n'a pas fonctionné pour moi, got element.findElements n'est pas une fonction.
Justin

251

Pour moi a fonctionné comme un charme

element(by.cssContainingText('option', 'BeaverBox Testing')).click();

J'espère que ça aide.


2
Note mineure - v0.22 uniquement (je suis juste allé remplacer mon code par celui-ci aujourd'hui et j'ai dû mettre à niveau pour l'obtenir)
PaulL

Existe-t-il un moyen de cibler les éléments en utilisant cela? Comme avoir des menus de sélection d'état en double.
Christopher Marshall

11
Christopher, ElementFinderest capable de chaîner, vous pouvez donc faire:element(by.css('.specific-select')).element(by.cssContainingText('option', 'BeaverBox Testing')).click();
Joel Kornbluh

6
Notez que vous pouvez obtenir des correspondances partielles, donc «Small» correspondra à «Extra Small».
TrueWill

3
Pour ajouter au commentaire de TrueWill: L'inconvénient de cette solution est que si vous avez deux options similaires, elle utilisera la dernière option trouvée - qui a généré des erreurs basées sur une mauvaise sélection. Ce qui a fonctionné pour moi, c'est stackoverflow.com/a/25333326/1945990
Mike W

29

Une approche élégante impliquerait de faire une abstraction similaire à ce que d'autres liaisons de langage sélénium offrent prêtes à l'emploi (par exemple, Selectclasse en Python ou Java).

Créons un wrapper pratique et masquons les détails de l'implémentation à l'intérieur:

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;

Exemple d'utilisation (notez à quel point il est lisible et facile à utiliser):

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 tirée du sujet suivant: Sélectionnez -> option abstraction .


FYI, a créé une demande de fonctionnalité: Sélectionnez -> option abstraction .


Pourquoi utilisez-vous «retour» dans les fonctions de sélection? Est-ce nécessaire?
Michiel

1
@Michiel bon point. Peut être nécessaire si vous souhaitez résoudre explicitement la promesse renvoyée par click(). Merci.
alecxe

20
element(by.model('parent_id')).sendKeys('BKN01');

1
C'est le plus correct pour moi. Avec cssContainingText pas vous vous assurez de capturer le champ souhaité. Pensez simplement si vous avez 2 boîtes de sélection avec les mêmes valeurs. +1
YoBre

4
Pardon, qu'est-ce que BKN01?
Gerfried

1
@Gerfried BKN01 est le texte que vous souhaitez sélectionner dans la liste déroulante.
MilanYadav

@GalBracha Désolé ne vous a pas compris
MilanYadav

Si vous avez une autre option qui partage le même préfex que BKN01, cela ne fonctionnera pas. Un aléatoire sera choisi.
zs2020

15

Pour accéder à une option spécifique, vous devez fournir le sélecteur nth-child ():

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

Cela ne répond pas à la question. Pour critiquer ou demander des éclaircissements à un auteur, laissez un commentaire sous sa publication.
jpw

@jpw est ma réponse erronée. ou est-ce simplement la formulation de ma réponse qui est fausse?
bekite

La formulation d'une réponse sous forme de question est pourquoi. Je ne sais pas si c'est la bonne réponse. Si c'est le cas, vous devez le formuler comme tel.
jpw

3
@bekite n'a pas fonctionné pour moi.Toujours obtenir l'erreur Erreur élément non visible lorsque j'ai essayé votre suggestion
Ranjan Bhambroo

8

C'est ainsi que j'ai fait ma sélection.

function switchType(typeName) {
     $('.dropdown').element(By.cssContainingText('option', typeName)).click();
};

5

Voici comment je l'ai fait:

$('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();

Pour attendre que angular termine le traitement en attente, utilisez la waitForAngular()méthode du rapporteur .
Avi Cherry

5

Essayez ceci, cela fonctionne pour moi:

element(by.model('formModel.client'))
    .all(by.tagName('option'))
    .get(120)
    .click();

4

Vous pouvez essayer ceci en espérant que cela fonctionnera

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
});

4

Une autre façon de définir un élément option:

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

var orderTest = element (by.model ('ctrl.supplier.orderTest')) orderTest. $ ('[value = "1"]'). click (); j'obtiens une erreur par (sélecteur css, [value = "3"])
Anuj KC

3

Pour sélectionner des éléments (options) avec des identifiants uniques comme ici:

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

J'utilise ceci:

element(by.css('[value="' + neededBarId+ '"]')).click();

3

Nous avons écrit une bibliothèque qui comprend 3 façons de sélectionner une 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>

Une caractéristique supplémentaire de ces fonctions est qu'elles attendent que l'élément soit affiché avant qu'une action sur le selectsoit effectuée.

Vous pouvez le trouver sur npm @ hetznercloud / protractor-test-helper . Des typages pour TypeScript sont également fournis.


2

Peut-être pas super élégant, mais efficace:

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

Cela envoie simplement la clé vers le bas sur la sélection que vous voulez, dans notre cas, nous utilisons modelSelector mais évidemment vous pouvez utiliser n'importe quel autre sélecteur.

Puis dans mon modèle d'objet de page:

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

Et du test:

myPage.selectMyOption(1);

2

Le problème est que les solutions qui fonctionnent sur des boîtes de sélection angulaires régulières ne fonctionnent pas avec le matériau angulaire md-select et md-option utilisant le rapporteur. Celui-ci a été posté par un autre, mais cela a fonctionné pour moi et je ne peux pas encore commenter son post (seulement 23 points de rep). De plus, je l'ai un peu nettoyé, au lieu de browser.sleep, j'ai utilisé 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
});

J'utilise juste ceci:element(by.model('selectModel')).click(); element(by.css('md-option[value="searchOptionValue"]')).click();
Luan Nguyen

Voici une autre approche: mdSelectElement.getAttribute('aria-owns').then( function(val) { let selectMenuContainer = element(by.id(val)); selectMenuContainer.element(by.css('md-option[value="foo"]')).click(); } );
gbruins

1

Il y a un problème avec la sélection des options dans Firefox que le piratage de Droogans corrige ici explicitement, en espérant que cela pourrait épargner à quelqu'un des problèmes: https://github.com/angular/protractor/issues/480 .

Même si vos tests réussissent localement avec Firefox, vous pouvez constater qu'ils échouent sur CircleCI ou TravisCI ou tout ce que vous utilisez pour le CI et le déploiement. Être conscient de ce problème dès le début m'aurait fait gagner beaucoup de temps :)


1

Aide à définir l'élément une option:

selectDropDownByText:function(optionValue) {
            element(by.cssContainingText('option', optionValue)).click(); //optionValue: dropDownOption
        }

1

Si ci-dessous se trouve la liste déroulante donnée-

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

Alors le code de protractorjs peut être-

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

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


1

Sélectionnez l'option par index:

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

1

J'ai un peu amélioré la solution écrite par PaulL. Tout d'abord, j'ai corrigé le code pour qu'il soit compatible avec la dernière API Protractor. Et puis je déclare la fonction dans la section 'onPrepare' d'un fichier de configuration Protractor en tant que membre de l' instance de navigateur , afin qu'elle puisse être référencée sous n'importe quelle spécification e2e.

  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();
        });
    };
  },

1

J'ai parcouru le filet pour trouver une réponse sur la façon de sélectionner une option dans la liste déroulante d'un modèle et j'ai utilisé cette combinaison qui m'a aidé avec le matériau angulaire.

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

il semble qu'en lançant le code sur une seule ligne, il pourrait trouver l'élément dans la liste déroulante.

Cela a pris beaucoup de temps pour cette solution, j'espère que cela aidera quelqu'un.


0

Nous voulions utiliser la solution élégante là-haut en utilisant le matériel angularjs, mais cela n'a pas fonctionné car il n'y a en fait aucune balise option / md-option dans le DOM tant que le md-select n'a pas été cliqué. Donc, la manière "élégante" n'a pas fonctionné pour nous (notez le matériau angulaire!) Voici ce que nous avons fait pour cela à la place, je ne sais pas si c'est la meilleure façon mais cela fonctionne définitivement maintenant

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
});

Nous devions avoir 4 sélections sélectionnées et pendant que la sélection est ouverte, il y a une superposition dans la manière de sélectionner la sélection suivante. C'est pourquoi nous devons attendre 500 ms pour nous assurer de ne pas avoir de problèmes avec les effets matériels toujours en action.


0

Une autre façon de définir un élément option:

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');

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')


});

0

Vous pouvez sélectionner des options de liste déroulante par valeur: $('#locregion').$('[value="1"]').click();


0

Voici comment le faire par valeur d'option ou par index . Cet exemple est un peu grossier, mais il montre comment faire ce que vous voulez:

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

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();
                   }
               }            
        }

    })

}

0

Nous pouvons créer une classe DropDown personnalisée pour cela et ajouter une méthode comme:

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

Aussi, pour vérifier quelle valeur est actuellement sélectionnée, nous pouvons avoir:

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

0

L'exemple ci-dessous est le moyen le plus simple. J'ai testé et passé la version Protractor5.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

Code complet

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();

  });
});

0

Il s'agit d'une réponse simple en une ligne dans laquelle angular a un localisateur spécial qui peut aider à sélectionner et à indexer dans la liste.

element.all(by.options('o.id as o.name for o in organizations')).get(Index).click()

Bien que ce code puisse résoudre la question, inclure une explication sur comment et pourquoi cela résout le problème aiderait vraiment à améliorer la qualité de votre publication et entraînerait probablement plus de votes à la hausse. N'oubliez pas que vous répondez à la question des lecteurs à l'avenir, pas seulement à la personne qui la pose maintenant. Veuillez modifier votre réponse pour ajouter des explications et donner une indication des limites et des hypothèses applicables. De l'avis
double-beep

-1

Sélectionnez l'option par propriété CSS

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

ou

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

Où locregion (id), organization.parent_id (nom du modèle) sont les attributs de l'élément select.

En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.