Jak sprawdzić, czy element ma klasę przy użyciu kątomierza?

90

Wypróbowuję Protractor to e2e test aplikacji Angular i nie wiem, jak wykryć, czy element ma określoną klasę, czy nie.

W moim przypadku test klika przycisk przesyłania, a teraz chcę wiedzieć, czy formularz [name = "getoffer"] ma klasę .ngDirty. Jakie mogą być rozwiązania?

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
    });
});
Allan Tatter
źródło

Odpowiedzi:

110

Jedną rzeczą, na którą musisz zwrócić uwagę przy użyciu toMatch(), tak jak w zaakceptowanej odpowiedzi, są częściowe dopasowania. Na przykład, powiedzmy, że masz element, który może mieć klasy correcti incorrect, i chcesz sprawdzić, czy ma on klasę correct. Jeśli miałbyś użyć expect(element.getAttribute('class')).toMatch('correct'), zwróci to prawdę, nawet jeśli element ma incorrectklasę.

Moja sugestia:

Jeśli chcesz akceptować tylko dokładne dopasowania, możesz utworzyć dla niego metodę pomocniczą:

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

Możesz go używać w ten sposób (korzystając z faktu, że expectautomatycznie wypełnia obietnice w Kątomierz):

expect(hasClass(element(by.name('getoffer')), 'ngDirty')).toBe(true);
Siergiej K.
źródło
1
To zadziałało dla mnie, z modem, że nazwa klasy musiała być w formacie łącznika, a nie przypadku wielbłąda, tj.expect(hasClass(element(by.name('getoffer')), 'ng-dirty')).toBe(true);
Ackroydd
Nie jestem pewien, co robi funkcja klasa, szczególnie gdzie lub jakie klasy są przekazywane do funkcji .then, czy ktoś mógłby mnie oświecić?
ErikAGriffin
@ErikAGriffin: Funkcja hasClass otrzymuje dwa argumenty - element html i nazwę klasy do sprawdzenia. Następnie funkcja pobiera atrybut „class” (klasy), który posiada element. Dzieli łańcuch klasy, aby uzyskać tablicę klas. Następnie sprawdza, czy szukana klasa znajduje się w jakimkolwiek dodatnim indeksie tablicy. Zakładam, że to sposób na sprawdzenie istnienia.
VSO
Przepisałem twoją czterowierszową funkcję do tego seksownego jednego linera ES6: hasClass = (element, className) => element.getAttribute ('class'). Then ((classes) => classes.split ('') .indexOf ( className)! == -1);
Laurens Mäkel
W TypeScript: funkcja async hasClass (elm: ElementFinder, className: string): Promise <boolean> {const classNamesStr: string = await elm.getAttribute ('class'); const classNamesArr: string [] = classNamesStr.split (''); return classNamesArr.includes (className); }
tedw
57

Jeśli używasz Kątomierza z Jasmine, możesz użyć toMatchdopasowania jako wyrażenia regularnego ...

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

Pamiętaj też, że toContainjeśli tego potrzebujesz, będzie pasować do elementów listy.

ryan.l
źródło
1
Nie jestem pewien, czy to idealny sposób na zrobienie tego, ale przynajmniej działa zgodnie z oczekiwaniami :)
Allan Tatter,
1
Nie, powiedziałbym, że prawdopodobnie nie. Spodziewałbym elementsię hasClass, że expectzwrócę obiekt metodą, którą można zawinąć w wywołanie ...
ryan.l
2
toContainmoże być lepszym wyborem niż toMatchw tym przypadku.
TrueWill
Jeśli chcesz ustrzec się przed częściowymi dopasowaniami, spróbuj czegoś takiego /(^|\s)ngDirty($|\s)/.
Andrew Myers
prawdopodobnie możesz użyć .not.toContaindo sprawdzenia, czy nie ma określonej klasy lub .toContaindo sprawdzenia, czy istnieje
Jack
18

Najprostszy to:

expect(element.getAttribute('class')).toContain("active");
Fidel Gonzo
źródło
To zwraca błąd java.util.ArrayList cannot be cast to java.lang.Stringw przeglądarce Microsoft Edge
Manolis
@Manolis Jak otrzymujesz wyjątki Java w konsoli internetowej dla angularjs?
vasia
4

W oparciu o odpowiedź od Sergeya K, możesz również dodać niestandardowy element dopasowujący, aby zrobić to:

(skrypt kawowy)

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

Następnie możesz go użyć w testach takich jak ten:

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

Jeśli tak się nie stanie, pojawi się następujący błąd:

 Message:
   Expected div#ugly to have class beautiful
 Stacktrace:
   Error: Expected div#ugly to have class 'beautiful'
Ed Hinchliffe
źródło
3

Czy próbowałeś...

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

lub odmiana powyższego ...

James Dykes
źródło
5
Problem w tym, że do formularza dołączona jest więcej niż jedna klasa.
Allan Tatter
2

Zrobiłem ten matcher, musiałem zawinąć go w obietnicę i użyć 2 zwrotów

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

w moim teście mogę teraz zrobić takie rzeczy:

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

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

działa to również, gdy element ma wiele klas lub gdy element nie ma w ogóle atrybutu klasy.

Michiel
źródło
1

Tutaj niestandardowy element dopasowujący Jasmine 1.3.x toHaveClassz .notobsługą negacji plus odczekaj do 5 sekund (lub cokolwiek wybierzesz).

W tym streszczeniu znajdź pełny niestandardowy element dopasowujący, który ma zostać dodany do bloku onPrepare

Przykładowe użycie:

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');
});
Leo Gallucci
źródło
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;
    });
}

Tak to przynajmniej robię, bez potrzeby używania funkcji oczekiwania. Ta funkcja po prostu zwraca wartość true, jeśli klasa znajduje się wewnątrz elementu, lub false, jeśli nie. Używa to również obietnic, więc możesz ich używać w następujący sposób:

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

Edycja: właśnie zdałem sobie sprawę, że jest to zasadniczo to samo, co najlepsza odpowiedź

Sir Neuman
źródło
1

Jednym ze sposobów osiągnięcia tego byłoby użycie xpath i użycie contains()

Przykład:

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

Możesz użyć parsera CSS, aby sobie z tym poradzić, sprawdzając, czy istnieje element o podanej klasie:

expect(element(by.css('.form[name="getoffer"].ngDirty')).isPresent()).toBe(true);
drzazga
źródło