Как проверить, есть ли у элемента класс с помощью Protractor?

90

Я пробую Protractor для тестирования приложения Angular e2e и не понял, как определить, имеет ли элемент определенный класс или нет.

В моем случае тест нажимает кнопку отправки, и теперь я хочу знать, имеет ли форма [name = "getoffer"] класс .ngDirty. Какие могут быть решения?

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
    });
});
Аллан Тэттер
источник

Ответы:

110

Одна ошибка, на которую вы должны обратить внимание при использовании toMatch(), как в принятом ответе, - это частичные совпадения. Например, предположим, что у вас есть элемент, который может иметь классы correctи incorrect, и вы хотите проверить, что у него есть класс correct. Если бы вы использовали expect(element.getAttribute('class')).toMatch('correct'), это вернет истину, даже если у элемента есть incorrectкласс.

Мое предложение:

Если вы хотите принимать только точные совпадения, вы можете создать для него вспомогательный метод:

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

Вы можете использовать его так (пользуясь тем фактом, что expectпромисы автоматически разрешаются в Protractor):

expect(hasClass(element(by.name('getoffer')), 'ngDirty')).toBe(true);
Сергей К
источник
1
Это сработало для меня, с модом, согласно которому имя класса должно быть в формате дефиса, а не в верблюжьем регистре, то естьexpect(hasClass(element(by.name('getoffer')), 'ng-dirty')).toBe(true);
Ackroydd
Я немного не понимаю, что делает класс функции, особенно где и какие классы при переходе в функцию .then, может ли кто-нибудь просветить меня?
ErikAGriffin
@ErikAGriffin: функции hasClass передаются два аргумента - элемент html и имя класса, который нужно проверить. Затем функция получает атрибут «class» (классы), который имеет элемент. Он разбивает строку класса, чтобы получить массив классов. Затем он проверяет, находится ли искомый класс в любом положительном индексе массива. Я предполагаю, что это способ проверить существование.
VSO
Я переписал вашу четырехстрочную функцию на этот сексуальный ES6 one liner: hasClass = (element, className) => element.getAttribute ('class'). Then ((classes) => classes.split ('') .indexOf ( className)! == -1);
Laurens Mäkel
В TypeScript: асинхронная функция hasClass (elm: ElementFinder, className: string): Promise <boolean> {const classNamesStr: string = await elm.getAttribute ('class'); const classNamesArr: строка [] = classNamesStr.split (''); вернуть classNamesArr.includes (имя класса); }
tedw
57

Если вы используете транспортир с Jasmine, вы можете использовать toMatchдля сопоставления как регулярное выражение ...

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

Также обратите внимание, что это toContainбудет соответствовать элементам списка, если вам это нужно.

ryan.l
источник
1
Я не уверен, что это идеальный способ сделать это, но, по крайней мере, он работает так, как ожидалось :)
Аллан Таттер
1
Нет, наверное, нет. Я бы ожидал elementвернуть объект с hasClassметодом, который вы могли бы обернуть внутри expectвызова ...
ryan.l
2
toContainможет быть лучшим выбором, чем toMatchв этом случае.
TrueWill
Если вы хотите защитить себя от частичных совпадений, попробуйте что-нибудь вроде /(^|\s)ngDirty($|\s)/.
Эндрю Майерс
вы, вероятно, можете использовать, .not.toContainчтобы проверить, нет ли у него определенного класса, или .toContainдля проверки, существует ли он
Джек
18

Самый простой:

expect(element.getAttribute('class')).toContain("active");
Фидель Гонзо
источник
Это возвращает ошибку java.util.ArrayList cannot be cast to java.lang.Stringв Microsoft Edge
Манолис,
@Manolis, как вы получаете исключения Java в веб-консоли для angularjs ??
Васия
4

Основываясь на ответе Сергея К., вы также можете добавить настраиваемый сопоставитель для этого:

(кофе)

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

Затем вы можете использовать его в таких тестах:

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

И если этого не произойдет, вы получите следующую ошибку:

 Message:
   Expected div#ugly to have class beautiful
 Stacktrace:
   Error: Expected div#ugly to have class 'beautiful'
Эд Хинчлифф
источник
3

Попытался ли ты...

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

или вариант вышеупомянутого ...

Джеймс Дайкс
источник
5
Проблема в том, что к форме прикреплено более одного класса.
Allan Tatter
2

Я сделал этот сопоставитель, мне пришлось обернуть его обещанием и использовать 2 возврата

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

в моем тесте я теперь могу делать вот так:

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

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

это также работает, когда элемент имеет несколько классов или когда элемент вообще не имеет атрибута класса.

Михиэль
источник
1

Здесь пользовательский toHaveClassсопоставитель Jasmine 1.3.x с .notподдержкой отрицания плюс ожидание до 5 секунд (или как вы укажете).

Найдите полный настраиваемый сопоставитель, который будет добавлен в ваш блок onPrepare в этой сути

Пример использования:

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

По крайней мере, так я делаю, без необходимости использовать функцию ожидания. Эта функция просто возвращает истину, если класс находится внутри элемента, и ложь, если нет. Здесь также используются обещания, поэтому вы могли бы использовать его так:

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

Изменить: я только что понял, что это по сути то же самое, что и главный ответ

Сэр Нойман
источник
1

Один из способов добиться этого - использовать xpath и использовать contains()

Пример:

var expectElementToHaveClass = function (className) {
    var path = by.xpath("//div[contains(@class,'"+ className +"')]");
    expect(element.all(path).count()).to.eventually.be.eq(1);
};
Чанту
источник
0

Вы можете использовать синтаксический анализатор CSS для обработки этого, проверив, существует ли элемент с данным классом:

expect(element(by.css('.form[name="getoffer"].ngDirty')).isPresent()).toBe(true);
шина
источник