Comment tester si un élément a une classe en utilisant Protractor?

90

J'essaie Protractor to e2e tester l'application Angular et je n'ai pas compris comment détecter si un élément a une classe spécifique ou non.

Dans mon cas, le test clique sur le bouton Soumettre et maintenant je veux savoir si le formulaire [name = "getoffer"] a la classe .ngDirty. Quelles peuvent être les solutions?

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

Réponses:

110

Un problème que vous devez rechercher avec l'utilisation toMatch(), comme dans la réponse acceptée, est les correspondances partielles. Par exemple, disons que vous avez un élément qui pourrait avoir les classes correctet incorrect, et que vous voulez tester qu'il a la classe correct. Si vous deviez utiliser expect(element.getAttribute('class')).toMatch('correct'), cela retournera true même si l'élément a la incorrectclasse.

Ma suggestion:

Si vous souhaitez n'accepter que les correspondances exactes, vous pouvez créer une méthode d'assistance pour cela:

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

Vous pouvez l'utiliser comme ceci (en profitant du fait que expectrésout automatiquement les promesses dans Protractor):

expect(hasClass(element(by.name('getoffer')), 'ngDirty')).toBe(true);
Sergey K
la source
1
Cela a fonctionné pour moi, avec le mod que le nom de la classe devait être au format trait d'union et non au format camel, c'estexpect(hasClass(element(by.name('getoffer')), 'ng-dirty')).toBe(true);
Ackroydd
Je ne sais pas très bien ce que fait la fonction a class, en particulier où ou quelles classes se trouvent lors de la transmission à la fonction .then, quelqu'un pourrait-il m'éclairer?
ErikAGriffin
@ErikAGriffin: La fonction hasClass reçoit deux arguments - un élément html et un nom de classe à vérifier. La fonction obtient alors l'attribut "class" (les classes) que possède l'élément. Il divise la chaîne de classe pour obtenir un tableau de classes. Il vérifie ensuite si la classe que vous recherchez est dans un index positif du tableau. Je suppose que c'est un moyen de vérifier l'existence.
VSO
J'ai réécrit votre fonction à quatre lignes sur cette seule ligne ES6 sexy: hasClass = (element, className) => element.getAttribute ('class'). Then ((classes) => classes.split ('') .indexOf ( className)! == -1);
Laurens Mäkel
Dans TypeScript: fonction async hasClass (elm: ElementFinder, className: string): Promise <boolean> {const classNamesStr: string = wait elm.getAttribute ('class'); const classNamesArr: string [] = classNamesStr.split (''); return classNamesArr.includes (className); }
tedw le
56

Si vous utilisez Protractor avec Jasmine, vous pouvez utiliser toMatchpour faire correspondre comme une expression régulière ...

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

Notez également que toContaincela correspondra aux éléments de la liste, si vous en avez besoin.

ryan.l
la source
1
Je ne suis pas sûr que ce soit la manière idéale de le faire, mais au moins cela fonctionne comme prévu :)
Allan Tatter
1
Non, je dirais que ce n'est probablement pas le cas. Je m'attendrais elementà renvoyer un objet avec une hasClassméthode, que vous pourriez envelopper à l'intérieur d'un expectappel ...
ryan.l
2
toContainpourrait être un meilleur choix que toMatchdans ce cas.
TrueWill
Si vous voulez vous prémunir contre les correspondances partielles, essayez quelque chose comme /(^|\s)ngDirty($|\s)/.
Andrew Myers
vous pouvez probablement utiliser .not.toContainpour vérifier s'il n'a pas la classe particulière ou .toContainpour vérifier si elle existe
Jack
18

Le plus simple est:

expect(element.getAttribute('class')).toContain("active");
Fidel Gonzo
la source
Cela renvoie une erreur java.util.ArrayList cannot be cast to java.lang.Stringsur Microsoft Edge
Manolis
@Manolis comment obtenez-vous des exceptions java dans la console Web pour angularjs ??
vasia le
4

Sur la base de la réponse de Sergey K, vous pouvez également ajouter un matcher personnalisé pour ce faire:

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

Ensuite, vous pouvez l'utiliser dans des tests comme celui-ci:

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

Et vous obtiendrez l'erreur suivante si ce n'est pas le cas:

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

As-tu essayé...

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

ou une variante de ce qui précède ...

James Dykes
la source
5
Le problème est que le formulaire est associé à plusieurs classes.
Allan Tatter
2

J'ai fait ce matcher, j'ai dû l'envelopper dans une promesse et utiliser 2 retours

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

dans mon test, je peux maintenant faire des trucs comme ceci:

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

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

cela fonctionne également lorsqu'un élément a plusieurs classes ou lorsqu'un élément n'a aucun attribut de classe.

Michiel
la source
1

Voici un toHaveClassmatcher personnalisé Jasmine 1.3.x avec .notsupport de négation et attendez jusqu'à 5 secondes (ou ce que vous spécifiez).

Trouvez le matcher personnalisé complet à ajouter à votre bloc onPrepare dans cet article

Exemple d'utilisation:

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

C'est ainsi que je le fais au moins, sans avoir besoin d'utiliser la fonction expect. Cette fonction renvoie simplement true si la classe est à l'intérieur de l'élément et false dans le cas contraire. Cela utilise également des promesses afin que vous l'utilisiez comme:

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

Edit: Je viens de réaliser que c'est essentiellement la même chose que la première réponse

Sir Neuman
la source
1

Une façon d'y parvenir serait d'utiliser xpath et d'utiliser contains()

Exemple:

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

Vous pouvez utiliser l'analyseur CSS pour gérer cela en vérifiant si un élément avec la classe donnée existe:

expect(element(by.css('.form[name="getoffer"].ngDirty')).isPresent()).toBe(true);
éclat
la source