Come verificare se un elemento ha una classe utilizzando Goniometro?


90

Sto provando Goniometro per testare e2e l'app Angular e non ho capito come rilevare se un elemento ha una classe specifica o meno.

Nel mio caso, il test fa clic sul pulsante di invio e ora voglio sapere se il form [name = "getoffer"] ha la classe .ngDirty. Quali possono essere le soluzioni?

describe('Contact form', function() {
    beforeEach(function(){
        browser.get('http://localhost:9000');
        element(by.linkText('Contact me')).click();
    });

    it('should fail form validation, all fields pristine', function() {
        element(by.css('.form[name="getoffer"] input[type="submit"]')).click();
        expect(element(by.name('getoffer'))).toHaveClass('ngDirty'); // <-- This line
    });
});

Risposte:


110

Un problema a cui devi prestare attenzione con l'utilizzo toMatch(), come nella risposta accettata, sono le corrispondenze parziali. Ad esempio, diciamo che hai un elemento che potrebbe avere le classi correcte incorrect, e vuoi verificare che abbia la classe correct. Se dovessi usare expect(element.getAttribute('class')).toMatch('correct'), restituirà vero anche se l'elemento ha la incorrectclasse.

Il mio consiglio:

Se vuoi accettare solo corrispondenze esatte, puoi creare un metodo di supporto per questo:

var hasClass = function (element, cls) {
    return element.getAttribute('class').then(function (classes) {
        return classes.split(' ').indexOf(cls) !== -1;
    });
};

Puoi usarlo in questo modo (sfruttando il fatto che expectrisolve automaticamente le promesse in Goniometro):

expect(hasClass(element(by.name('getoffer')), 'ngDirty')).toBe(true);

1
Questo ha funzionato per me, con la mod che il nome della classe doveva essere formato trattino e non caso cammello, cioèexpect(hasClass(element(by.name('getoffer')), 'ng-dirty')).toBe(true);
Ackroydd

Sono poco chiaro su cosa la funzione ha la classe sta facendo, in particolare dove o quali classi sono quando vengono passate alla funzione .then, qualcuno potrebbe illuminarmi?
ErikAGriffin

@ErikAGriffin: alla funzione hasClass vengono passati due argomenti: un elemento html e un nome di una classe da verificare. La funzione quindi ottiene l'attributo "class" (le classi) di cui dispone l'elemento. Divide la stringa della classe per ottenere un array di classi. Quindi controlla se la classe che stai cercando è in un indice positivo dell'array. Presumo sia un modo per verificare l'esistenza.
VSO

Ho riscritto la tua funzione a quattro righe in questa sexy ES6 one liner: hasClass = (element, className) => element.getAttribute ('class'). Then ((classes) => classes.split ('') .indexOf ( className)! == -1);
Laurens Mäkel

In TypeScript: async, la funzione hasClass (elm: ElementFinder, className: string): Promise <boolean> {const classNamesStr: string = await elm.getAttribute ('class'); const classNamesArr: string [] = classNamesStr.split (''); return classNamesArr.includes (className); }
tedw

56

Se stai usando Goniometro con Jasmine, potresti usare toMatchper abbinare come espressione regolare ...

expect(element(by.name('getoffer')).getAttribute('class')).toMatch('ngDirty');

Inoltre, tieni presente che toContaincorrisponderà agli elementi dell'elenco, se necessario.


1
Non sono sicuro che sia il modo ideale per farlo, ma almeno funziona come previsto :)
Allan Tatter

1
No, direi che probabilmente non lo è. Mi aspetto elementdi restituire un oggetto con un hasClassmetodo, che potresti racchiudere all'interno di una expectchiamata ...
ryan.l

2
toContainpotrebbe essere una scelta migliore rispetto toMatcha questo caso.
TrueWill

Se vuoi proteggerti dalle partite parziali, prova qualcosa di simile /(^|\s)ngDirty($|\s)/.
Andrew Myers

probabilmente puoi usare .not.toContainper verificare se non ha la classe particolare o .toContainper verificare se esiste
Jack

18

Il più semplice è:

expect(element.getAttribute('class')).toContain("active");

Questo restituisce un errore java.util.ArrayList cannot be cast to java.lang.Stringsu Microsoft Edge
Manolis

@Manolis come stai ottenendo le eccezioni java nella console web per angularjs ??
vasia

4

In base alla risposta di Sergey K, potresti anche aggiungere un abbinatore personalizzato per farlo:

(coffeescript)

  beforeEach(()->
    this.addMatchers({
      toHaveClass: (expected)->
        @message = ()->
          "Expected #{@actual.locator_.value} to have class '#{expected}'"

        @actual.getAttribute('class').then((classes)->
          classes.split(' ').indexOf(expected) isnt -1
        )
    })
  )

Quindi puoi usarlo in test come questo:

expect($('div#ugly')).toHaveClass('beautiful')

E riceverai il seguente errore se non lo fa:

 Message:
   Expected div#ugly to have class beautiful
 Stacktrace:
   Error: Expected div#ugly to have class 'beautiful'

3

Hai provato...

var el = element(by.name('getoffer'));
expect(e.getAttribute('class')).toBe('ngDirty')

o una variazione di quanto sopra ...


5
Il problema è che il modulo ha più di una classe allegata.
Allan Tatter

2

Ho creato questo matcher, ho dovuto racchiuderlo in una promessa e utilizzare 2 resi

this.addMatchers({
    toHaveClass: function(a) {
        return this.actual.getAttribute('class').then(function(cls){
            var patt = new RegExp('(^|\\s)' + a + '(\\s|$)');
            return patt.test(cls);
        });
    }
});

nel mio test ora posso fare cose in questo modo:

   var myDivs = element.all(by.css('div.myClass'));
   expect(myDivs.count()).toBe(3);

   // test for class
   expect(myDivs.get(0)).not.toHaveClass('active');

funziona anche quando un elemento ha più classi o quando un elemento non ha alcun attributo di classe.


1

Qui un toHaveClassmatcher personalizzato Jasmine 1.3.x con .notsupporto alla negazione e attendi fino a 5 secondi (o qualunque cosa tu specifichi).

Trova il matcher personalizzato completo da aggiungere al tuo blocco onPrepare in questa sintesi

Utilizzo del campione:

it('test the class finder custom matcher', function() {
    // These guys should pass OK given your user input
    // element starts with an ng-invalid class:
    expect($('#user_name')).toHaveClass('ng-invalid');
    expect($('#user_name')).not.toHaveClass('ZZZ');
    expect($('#user_name')).toNotHaveClass('ZZZ');
    expect($('#user_name')).not.toNotHaveClass('ng-invalid');
    // These guys should each fail:
    expect($('#user_name')).toHaveClass('ZZZ');
    expect($('#user_name')).not.toHaveClass('ng-invalid');
    expect($('#user_name')).toNotHaveClass('ng-invalid');
    expect($('#user_name')).not.toNotHaveClass('ZZZ');
});

1
function checkHasClass (selector, class_name) {
    // custom function returns true/false depending if selector has class name

    // split classes for selector into a list
    return $(selector).getAttribute('class').then(function(classes){
        var classes = classes.split(' ');
        if (classes.indexOf(class_name) > -1) return true;
        return false;
    });
}

Almeno così è come lo faccio, senza la necessità di utilizzare la funzione di attesa. Questa funzione restituisce semplicemente true se la classe è all'interno dell'elemento e false in caso contrario. Questo utilizza anche le promesse, quindi lo useresti come:

checkHasClass('#your-element', 'your-class').then(function(class_found){
    if (class_found) console.log("Your element has that class");
});

Modifica: mi sono appena reso conto che è essenzialmente la stessa della risposta principale


1

Un modo per ottenere ciò sarebbe usare xpath e use contains()

Esempio:

var expectElementToHaveClass = function (className) {
    var path = by.xpath("//div[contains(@class,'"+ className +"')]");
    expect(element.all(path).count()).to.eventually.be.eq(1);
};

0

Puoi utilizzare il parser CSS per gestirlo controllando se esiste un elemento con la classe data:

expect(element(by.css('.form[name="getoffer"].ngDirty')).isPresent()).toBe(true);
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.