如何使用Protractor测试元素是否具有类?

93

我正在尝试使用Protractor进行端到端测试Angular应用程序,但还没有弄清楚如何检测元素是否具有特定的类。

在我的情况下,测试会单击提交按钮,现在我想知道form[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
    });
});
11个回答

114
< p >使用toMatch()时需要注意一个问题,如在被接受的答案中所述,它会匹配部分字符。例如,假设您有一个元素可能有类“ correct”和“ incorrect”,您想测试它是否有类“ correct”。如果您使用expect(element.getAttribute('class')).toMatch('correct'),即使该元素具有类“ incorrect”,它也会返回true。

我的建议:

如果您只想接受完全匹配的结果,则可以创建一个帮助方法:

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
我对函数has class的作用还有点不清楚,特别是当传递到.then函数时,classes是在哪里或者是什么类,请问有人能给我解释一下吗? - ErikAGriffin
hasClass函数接收两个参数 - 一个HTML元素和要检查的类的名称。该函数然后获取元素具有的“class”属性(类),并将类字符串分割以获取类数组。然后它检查你正在寻找的类是否在数组的任何正索引中。我认为这是一种检查存在的方式。 - VSO
我已经将你的四行函数重写为这个性感的ES6一行代码:hasClass = (element, className) => element.getAttribute('class').then( (classes) => classes.split(' ').indexOf(className) !== -1 ); - Laurens Mäkel
在TypeScript中: async function 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
如果您正在使用Jasmine作为Protractor的测试框架,您可以使用toMatch将其作为正则表达式进行匹配...
expect(element(by.name('getoffer')).getAttribute('class')).toMatch('ngDirty');

另外要注意的是,如果需要匹配列表项,toContain也会进行匹配。


1
我不确定这是否是理想的方法,但至少它按预期工作 :) - Allan Tatter
1
不,我认为可能不是这样。我期望 element 返回一个带有 hasClass 方法的对象,你可以将其包装在 expect 调用中... - ryan.l
2
在这种情况下,toContain可能比toMatch更好的选择。 - TrueWill
如果你想防止部分匹配,可以尝试使用类似于 /(^|\s)ngDirty($|\s)/ 的东西。 - Andrew Myers
你可以使用.not.toContain来检查它是否没有特定的类,或者.toContain来检查它是否存在。 - Jack

19

最简单的方式是:

expect(element.getAttribute('class')).toContain("active");

在Microsoft Edge上,这会返回错误“java.util.ArrayList无法转换为java.lang.String”。 - Manolis
1
@Manolis,你是如何在AngularJS的Web控制台中获取Java异常的? - vasia

4
根据Sergey K的回答,您也可以添加一个自定义匹配器来实现此功能:
(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
        )
    })
  )

然后你可以像这样在测试中使用它:
expect($('div#ugly')).toHaveClass('beautiful')

如果没有成功,你将会收到以下错误信息:
 Message:
   Expected div#ugly to have class beautiful
 Stacktrace:
   Error: Expected div#ugly to have class 'beautiful'

4

你试过这个吗?

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

或以上变种。

5
问题在于这个表单有多个类别被附加上了。 - Allan Tatter

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

这至少是我在不需要使用expect函数的情况下进行的操作。此函数仅在类在元素内时返回true,否则返回false。此方法还使用了promises,因此您可以像这样使用它:

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

编辑:我刚意识到这与顶部答案基本相同。


2
我制作了这个匹配器,不得不将它包装在一个Promise中,并使用了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');

当一个元素有多个类时,或者一个元素根本没有class属性时,这个方法同样适用。


1
你可以使用CSS解析器来处理此问题,方法是检查是否存在具有给定类的元素:
expect(element(by.css('.form[name="getoffer"].ngDirty')).isPresent()).toBe(true);

1

这里是一个Jasmine 1.3.x自定义toHaveClass匹配器,支持否定.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

实现这一点的一种方法是使用xpath并使用contains()

示例:

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

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接