Come selezionare l'opzione nei test di protractorjs e2e a discesa


121

Sto cercando di selezionare un'opzione da un menu a discesa per i test e2e angolari utilizzando il goniometro.

Ecco lo snippet di codice dell'opzione di selezione:

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

Ho provato:

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

Questo mi dà il seguente errore:

È stata specificata una stringa non valida o non valida. Informazioni build: versione: "2.35.0", revisione: "c916b9d", ora: "2013-08-12 15:42:01" Informazioni di sistema: os.name: "Mac OS X" , os.arch: 'x86_64', os.version: '10 .9 ', java.version:' 1.6.0_65 'Informazioni driver: driver.version: sconosciuto

Ho anche provato:

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

Questo mi dà il seguente errore:

ElementNotVisibleError: L'elemento non è attualmente visibile e quindi non può essere interagito con la durata o il timeout del comando: 9 millisecondi Informazioni build: versione: '2.35.0', revisione: 'c916b9d', ora: '2013-08-12 15:42: 01 'Informazioni di sistema: os.name:' Mac OS X ', os.arch:' x86_64 ', os.version: '10 .9', java.version: '1.6.0_65' ID sessione: bdeb8088-d8ad-0f49-aad9 -82201c45c63f Informazioni sui driver: 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, handleAlerts = true, browserConnectionEnabled = true, nativeEvents = false, webStorageEnabled = true, applicationCacheEnabled = false, takesScreenshot = true}]

Qualcuno può aiutarmi con questo problema o fare luce su cosa potrei fare di sbagliato qui.

Risposte:


88

Ho avuto un problema simile e alla fine ho scritto una funzione di supporto che seleziona i valori a discesa.

Alla fine ho deciso che stavo selezionando in base al numero di opzione, e quindi ho scritto un metodo che prende un elemento e il optionNumber e seleziona quell'opzioneNumber. Se optionNumber è nullo, non seleziona nulla (lasciando deselezionato il menu a discesa).

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

Ho scritto un post sul blog se vuoi maggiori dettagli, copre anche la verifica del testo dell'opzione selezionata in un menu a discesa: http://technpol.wordpress.com/2013/12/01/protractor-and-dropdowns-validation/


1
Non ha funzionato per me, ottenuto element.findElements non è una funzione.
Justin

251

Per me ha funzionato a meraviglia

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

Spero che sia d'aiuto.


2
Nota minore - solo v0.22 (sono appena andato a sostituire il mio codice con questo oggi e ho dovuto eseguire l'aggiornamento per ottenerlo)
PaulL

C'è un modo per indirizzare gli elementi utilizzando questo? Come avere menu di selezione dello stato duplicati.
Christopher Marshall

11
Christopher, ElementFinderè incatenabile, quindi puoi fare:element(by.css('.specific-select')).element(by.cssContainingText('option', 'BeaverBox Testing')).click();
Joel Kornbluh

6
Nota che puoi ottenere corrispondenze parziali, quindi "Piccolo" corrisponderà a "Extra piccolo".
TrueWill

3
Per aggiungere al commento di TrueWill: Il lato negativo di questa soluzione è che se hai due opzioni simili utilizzerà l'ultima opzione trovata, che ha generato errori basati sulla selezione sbagliata. Quello che ha funzionato per me è stato stackoverflow.com/a/25333326/1945990
Mike W

29

Un approccio elegante comporterebbe la creazione di un'astrazione simile a ciò che altri legami del linguaggio selenio offrono fuori dagli schemi (ad esempio, una Selectclasse in Python o Java).

Creiamo un comodo wrapper e nascondiamo i dettagli di implementazione all'interno:

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;

Esempio di utilizzo (nota quanto sia leggibile e facile da usare):

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

Soluzione tratta dal seguente argomento: Seleziona -> opzione astrazione .


Cordiali saluti, ha creato una richiesta di funzionalità: Seleziona -> opzione astrazione .


Perché usi "return" nelle funzioni selezionate? È necessario?
Michiel

1
@ Michiel buon punto. Potrebbe essere necessario se desideri risolvere esplicitamente la promessa restituita da click(). Grazie.
alecxe

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

1
È il più corretto per me. Con cssContainText non ti assicuri di catturare il campo desiderato. Pensa se hai 2 selectbox con gli stessi valori. +1
YoBre

4
Non ho capito, cos'è BKN01?
Gerfried

1
@ Gerfried BKN01 è il testo che desideri selezionare dal menu a discesa.
Milan Yadav

@GalBracha Scusa non ti
ho capito

Se hai un'altra opzione che condivide lo stesso prefex di BKN01, non funzionerà. Ne verrà scelto uno a caso.
zs2020

15

Per accedere a un'opzione specifica è necessario fornire il selettore nth-child ():

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

Questo non fornisce una risposta alla domanda. Per criticare o richiedere chiarimenti a un autore, lascia un commento sotto il suo post.
jpw

@jpw è la mia risposta sbagliata. o è semplicemente la formulazione della mia risposta che è sbagliata?
bekite

La formulazione di una risposta come domanda è perché. Non so se è la risposta corretta. Se lo è, dovresti formularlo come tale.
jpw

3
@bekite non ha funzionato per me Continuo a ricevere l'errore Elemento Errore non visibile quando ho provato il tuo suggerimento
Ranjan Bhambroo

8

Ecco come ho fatto la mia selezione.

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

5

Ecco come l'ho fatto:

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

Per attendere che angular termini in attesa di elaborazione, utilizzare il waitForAngular()metodo del goniometro .
Avi Cherry

5

Prova questo, per me funziona:

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

4

Puoi provare questa speranza che funzioni

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

Un altro modo per impostare un elemento opzione:

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

var orderTest = element (by.model ('ctrl.supplier.orderTest')) orderTest. $ ('[value = "1"]'). click (); ricevo un errore da (selettore CSS, [value = "3"])
Anuj KC

3

Per selezionare elementi (opzioni) con ID univoci come qui:

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

Sto usando questo:

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

3

Abbiamo scritto una libreria che include 3 modi per selezionare un'opzione:

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>

Una caratteristica aggiuntiva di queste funzioni è che attendono che l'elemento venga visualizzato prima di eseguire qualsiasi azione su select.

Puoi trovarlo su npm @ hetznercloud / protractor-test-helper . Vengono fornite anche le digitazioni per TypeScript.


2

Forse non super elegante, ma efficiente:

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

Questo invia semplicemente la chiave sulla selezione che desideri, nel nostro caso, stiamo usando modelSelector ma ovviamente puoi usare qualsiasi altro selettore.

Quindi nella mia pagina modello di oggetti:

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

E dal test:

myPage.selectMyOption(1);

2

Il problema è che le soluzioni che funzionano su normali caselle di selezione angolare non funzionano con il materiale angolare md-select e md-option utilizzando il goniometro. Questo è stato pubblicato da un altro, ma ha funzionato per me e non sono ancora in grado di commentare il suo post (solo 23 punti rep). Inoltre, l'ho ripulito un po ', invece di browser.sleep, ho usato 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
});

Uso solo questo:element(by.model('selectModel')).click(); element(by.css('md-option[value="searchOptionValue"]')).click();
Luan Nguyen,

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

1

C'è un problema con la selezione delle opzioni in Firefox che l'hack di Droogans risolve che voglio menzionare qui esplicitamente, sperando che possa risparmiare qualche problema a qualcuno: https://github.com/angular/protractor/issues/480 .

Anche se i tuoi test vengono superati localmente con Firefox, potresti scoprire che non riescono su CircleCI o TravisCI o qualunque cosa tu stia utilizzando per CI e distribuzione. Essere consapevoli di questo problema fin dall'inizio mi avrebbe risparmiato molto tempo :)


1

Helper per impostare l'elemento an option:

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


1

Seleziona un'opzione per Indice:

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

1

Ho migliorato un po 'la soluzione scritta da PaulL. Prima di tutto ho corretto il codice in modo che fosse compatibile con l'ultima API di Protractor. E poi dichiaro la funzione nella sezione "onPrepare" di un file di configurazione di Protractor come membro dell'istanza del browser , quindi può essere referenziato da qualsiasi specifica 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

Ho cercato una risposta su come selezionare un'opzione in un menu a discesa del modello e ho usato questa combinazione che mi ha aiutato con il materiale angolare.

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

sembra che quando si lancia il codice tutto in una riga potrebbe trovare l'elemento nel menu a discesa.

Ho impiegato molto tempo per questa soluzione, spero che questo aiuti qualcuno.


0

Volevamo utilizzare l'elegante soluzione lassù utilizzando materiale angularjs ma non ha funzionato perché in realtà non ci sono tag option / md-option nel DOM finché non si fa clic su md-select. Quindi il modo "elegante" non ha funzionato per noi (nota il materiale angolare!) Ecco cosa abbiamo fatto invece per esso, non so se è il modo migliore ma sta sicuramente funzionando ora

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

Dovevamo avere 4 selezioni selezionate e mentre la selezione è aperta, c'è una sovrapposizione nel modo in cui selezionare la selezione successiva. ecco perché dobbiamo aspettare 500 ms per assicurarci di non avere problemi con gli effetti materiali ancora in azione.


0

Un altro modo per impostare un elemento opzione:

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

È possibile selezionare le opzioni del menu a discesa in base al valore: $('#locregion').$('[value="1"]').click();


0

Ecco come farlo tramite il valore dell'opzione o l'indice . Questo esempio è un po 'rozzo, ma mostra come fare quello che vuoi:

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

Possiamo creare una classe DropDown personalizzata per questo e aggiungere un metodo come:

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

Inoltre, per verificare quale valore è attualmente selezionato, possiamo avere:

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

0

L'esempio seguente è il modo più semplice. Ho testato e superato la versione Goniometro5.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

Codice completo

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

Questa è una semplice risposta di una riga in cui angular ha un localizzatore speciale che può aiutare a selezionare e indicizzare dall'elenco.

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

Sebbene questo codice possa risolvere la domanda, inclusa una spiegazione di come e perché questo risolve il problema aiuterebbe davvero a migliorare la qualità del tuo post e probabilmente si tradurrebbe in più voti positivi. Ricorda che stai rispondendo alla domanda per i lettori in futuro, non solo alla persona che chiede ora. Si prega di modificare la risposta per aggiungere spiegazioni e dare un'indicazione di ciò si applicano le limitazioni e le assunzioni. Dalla recensione
doppio segnale acustico

-1

Seleziona l'opzione per proprietà CSS

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

o

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

Dove locregion (id), organization.parent_id (nome modello) sono gli attributi dell'elemento select.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.