¿Cómo probar si un elemento tiene clase usando Transportador?

90

Estoy probando Protractor para probar la aplicación Angular e2e y no he descubierto cómo detectar si un elemento tiene una clase específica o no.

En mi caso, la prueba hace clic en el botón enviar y ahora quiero saber si el formulario [name = "getoffer"] tiene clase .ngDirty. ¿Cuáles pueden ser las soluciones?

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
fuente

Respuestas:

110

Un problema que debe tener en cuenta al usar toMatch(), como en la respuesta aceptada, son coincidencias parciales. Por ejemplo, digamos que tiene un elemento que podría tener las clases correcty incorrect, y desea probar que tiene la clase correct. Si lo usara expect(element.getAttribute('class')).toMatch('correct'), devolverá verdadero incluso si el elemento tiene laincorrect clase.

Mi sugerencia:

Si solo desea aceptar coincidencias exactas, puede crear un método auxiliar para ello:

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

Puedes usarlo así (aprovechando que expectresuelve automáticamente las promesas en Transportador):

expect(hasClass(element(by.name('getoffer')), 'ngDirty')).toBe(true);
Sergey K
fuente
1
Esto funcionó para mí, con la modificación de que el nombre de la clase tenía que ser un formato de guión, no un caso de camello, es decirexpect(hasClass(element(by.name('getoffer')), 'ng-dirty')).toBe(true);
Ackroydd
No tengo muy claro qué está haciendo la función tiene la clase, particularmente dónde o qué clases está cuando se pasa a la función. Then, ¿podría alguien aclararme?
ErikAGriffin
@ErikAGriffin: La función hasClass obtiene dos argumentos: un elemento html y el nombre de una clase para verificar. La función luego obtiene el atributo "clase" (las clases) que tiene el elemento. Divide la cadena de la clase para obtener una matriz de clases. Luego verifica si la clase que está buscando está en algún índice positivo de la matriz. Supongo que es una forma de comprobar la existencia.
VSO
He reescrito su función de cuatro líneas en este sexy ES6 one liner: hasClass = (element, className) => element.getAttribute ('class'). Then ((classes) => classes.split ('') .indexOf ( className)! == -1);
Laurens Mäkel
En TypeScript: función asíncrona hasClass (elm: ElementFinder, className: string): Promise <boolean> {const classNamesStr: string = await elm.getAttribute ('clase'); const classNamesArr: string [] = classNamesStr.split (''); return classNamesArr.includes (className); }
tedw
56

Si está usando Transportador con Jasmine, podría usar toMatchpara hacer coincidir como una expresión regular ...

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

Además, tenga en cuenta que toContaincoincidirá con los elementos de la lista, si lo necesita.

ryan.l
fuente
1
No estoy seguro de si esa es la forma ideal de hacer esto, pero al menos funciona como se esperaba :)
Allan Tatter
1
No, yo diría que probablemente no lo sea. Esperaría elementdevolver un objeto con un hasClassmétodo, que podría envolver dentro de una expectllamada ...
ryan.l
2
toContainpodría ser una mejor opción que toMatchen este caso.
TrueWill
Si desea protegerse contra coincidencias parciales, intente algo como /(^|\s)ngDirty($|\s)/.
Andrew Myers
probablemente pueda usar .not.toContainpara verificar si no tiene la clase en particular o .toContainpara verificar si existe
Jack
18

El más simple es:

expect(element.getAttribute('class')).toContain("active");
Fidel Gonzo
fuente
Esto devuelve un error java.util.ArrayList cannot be cast to java.lang.Stringen Microsoft Edge
Manolis
@Manolis, ¿cómo está obteniendo excepciones de java en la consola web para angularjs?
Vasia
4

Según la respuesta de Sergey K, también puede agregar un comparador personalizado para hacer esto:

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

Entonces puedes usarlo en pruebas como esta:

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

Y obtendrá el siguiente error si no es así:

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

Has probado...

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

o una variación de lo anterior ...

James Dykes
fuente
5
El problema es que el formulario tiene más de una clase adjunta.
Allan Tatter
2

Hice este emparejador, tuve que envolverlo en una promesa y usar 2 devoluciones

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

en mi prueba ahora puedo hacer cosas como esta:

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

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

esto también funciona cuando un elemento tiene varias clases o cuando un elemento no tiene ningún atributo de clase.

Michiel
fuente
1

Aquí, un toHaveClasscomparador personalizado Jasmine 1.3.x con .notsoporte de negación más espera hasta 5 segundos (o lo que especifique).

Encuentre el emparejador personalizado completo que se agregará en su bloque onPrepare en esta esencia

Uso de muestra:

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

Así es como lo hago al menos, sin la necesidad de usar la función de espera. Esta función simplemente devuelve verdadero si la clase está dentro del elemento y falso si no. Esto también usa promesas, por lo que lo usaría como:

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

Editar: me acabo de dar cuenta de que esto es esencialmente lo mismo que la respuesta principal

Sir Neuman
fuente
1

Una forma de lograr esto sería usar xpath y usar contains()

Ejemplo:

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

Puede usar el analizador CSS para manejar esto verificando si existe un elemento con la clase dada:

expect(element(by.css('.form[name="getoffer"].ngDirty')).isPresent()).toBe(true);
férula
fuente