Wie teste ich mit Protractor, ob ein Element eine Klasse hat?

90

Ich probiere Protractor aus, um die Angular-App e2e zu testen, und habe nicht herausgefunden, wie ich feststellen kann, ob ein Element eine bestimmte Klasse hat oder nicht.

In meinem Fall klickt der Test auf die Schaltfläche "Senden" und jetzt möchte ich wissen, ob form [name = "getoffer"] die Klasse .ngDirty hat. Was können die Lösungen sein?

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
quelle

Antworten:

110

Ein Problem, auf das Sie bei der Verwendung achten müssen toMatch(), wie in der akzeptierten Antwort, sind Teilübereinstimmungen. Angenommen, Sie haben ein Element, das möglicherweise die Klassen correctund enthält incorrect, und Sie möchten testen, ob es die Klasse enthält correct. Wenn Sie verwenden expect(element.getAttribute('class')).toMatch('correct'), gibt dies true zurück, auch wenn das Element die incorrectKlasse hat.

Mein Vorschlag:

Wenn Sie nur genaue Übereinstimmungen akzeptieren möchten, können Sie eine Hilfsmethode dafür erstellen:

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

Sie können es so verwenden (unter Ausnutzung der Tatsache, dass expectVersprechen in Winkelmesser automatisch aufgelöst werden):

expect(hasClass(element(by.name('getoffer')), 'ngDirty')).toBe(true);
Sergey K.
quelle
1
Dies funktionierte für mich mit dem Mod, dass der Klassenname Bindestrich-Format sein musste, nicht Kamel-Fall, dhexpect(hasClass(element(by.name('getoffer')), 'ng-dirty')).toBe(true);
Ackroydd
Ich bin mir nicht sicher, was die Funktion der Klasse tut, insbesondere wo oder welche Klassen sich befinden, wenn sie an die .then-Funktion übergeben werden. Könnte mich jemand aufklären?
ErikAGriffin
@ErikAGriffin: Der Funktion hasClass werden zwei Argumente übergeben - ein HTML-Element und der Name einer Klasse, nach der gesucht werden soll. Die Funktion erhält dann das Attribut "class" (die Klassen), über die das Element verfügt. Es teilt die Klassenzeichenfolge auf, um ein Array von Klassen zu erhalten. Anschließend wird geprüft, ob sich die gesuchte Klasse in einem positiven Index des Arrays befindet. Ich nehme an, es ist eine Möglichkeit, die Existenz zu überprüfen.
VSO
Ich habe Ihre vierzeilige Funktion in diesen sexy ES6-Einzeiler umgeschrieben: hasClass = (element, className) => element.getAttribute ('class'). Then ((classes) => classes.split ('') .indexOf ( className)! == -1);
Laurens Mäkel
In TypeScript: asynchrone Funktion hasClass (elm: ElementFinder, className: string): Promise <boolean> {const classNamesStr: string = warte auf elm.getAttribute ('class'); const classNamesArr: string [] = classNamesStr.split (''); return classNamesArr.includes (className); }
17.
56

Wenn Sie Protractor mit Jasmine verwenden, können Sie es toMatchals regulären Ausdruck verwenden ...

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

Beachten Sie auch, dass toContaindie Listenelemente übereinstimmen, wenn Sie dies benötigen.

ryan.l
quelle
1
Ich bin nicht sicher, ob das der ideale Weg ist, aber zumindest funktioniert es wie erwartet :)
Allan Tatter
1
Nein, ich würde sagen, dass es wahrscheinlich nicht so ist. Ich würde erwarten element, ein Objekt mit einer hasClassMethode zurückzugeben, die Sie in einen expectAufruf
einschließen
2
toContainkönnte eine bessere Wahl sein als toMatchin diesem Fall.
TrueWill
Wenn Sie sich vor Teilspielen schützen möchten, versuchen Sie etwas wie /(^|\s)ngDirty($|\s)/.
Andrew Myers
Sie können wahrscheinlich verwenden, um .not.toContainzu überprüfen, ob es nicht die bestimmte Klasse hat, oder .toContainum zu überprüfen, ob es existiert
Jack
18

Am einfachsten ist:

expect(element.getAttribute('class')).toContain("active");
Fidel Gonzo
quelle
Dies gibt Fehler java.util.ArrayList cannot be cast to java.lang.Stringauf Microsoft Edge
Manolis
@Manolis Wie bekommst du Java-Ausnahmen in der Webkonsole für AngularJs?
Vasa
4

Basierend auf der Antwort von Sergey K können Sie dazu auch einen benutzerdefinierten Matcher hinzufügen:

(Kaffeeskript)

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

Dann können Sie es in Tests wie diesen verwenden:

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

Andernfalls wird der folgende Fehler angezeigt:

 Message:
   Expected div#ugly to have class beautiful
 Stacktrace:
   Error: Expected div#ugly to have class 'beautiful'
Ed Hinchliffe
quelle
3

Hast du es versucht...

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

oder eine Variation der oben genannten ...

James Dykes
quelle
4
Das Problem ist, dass dem Formular mehr als eine Klasse zugeordnet ist.
Allan Tatter
2

Ich habe diesen Matcher gemacht, ich musste ihn in ein Versprechen einwickeln und 2 Retouren verwenden

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

In meinem Test kann ich jetzt so etwas machen:

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

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

Dies funktioniert auch, wenn ein Element mehrere Klassen hat oder wenn ein Element überhaupt kein Klassenattribut hat.

Michiel
quelle
1

Hier ein Jasmine 1.3.x Custom toHaveClassMatcher mit Negationsunterstützung .notund einer Wartezeit von bis zu 5 Sekunden (oder was auch immer Sie angeben).

In dieser Übersicht finden Sie den vollständigen benutzerdefinierten Matcher, der Ihrem onPrepare-Block hinzugefügt werden soll

Beispielnutzung:

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

So mache ich es zumindest, ohne die Expect-Funktion verwenden zu müssen. Diese Funktion gibt einfach true zurück, wenn sich die Klasse innerhalb des Elements befindet, und false, wenn nicht. Dies verwendet auch Versprechen, so dass Sie es wie folgt verwenden würden:

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

Bearbeiten: Ich habe gerade festgestellt, dass dies im Wesentlichen die gleiche wie die Top-Antwort ist

Sir Neuman
quelle
1

Eine Möglichkeit, dies zu erreichen, wäre die Verwendung von xpath und use contains()

Beispiel:

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

Sie können den CSS-Parser verwenden, um dies zu handhaben, indem Sie überprüfen, ob ein Element mit der angegebenen Klasse vorhanden ist:

expect(element(by.css('.form[name="getoffer"].ngDirty')).isPresent()).toBe(true);
Schiene
quelle