Testing link style changes

2019-02-11 01:48发布

In one of our tests, we are testing the link (a element) style changes after a mouse over.

By default, the link has a black font without decoration, but on mouse over the font changes to blue and the link text becomes underlined. Here is the relevant test:

it("should change font style on mouse over", function () {
    expect(scope.page.forgotPassword.getCssValue("color")).toEqual("rgba(11, 51, 60, 1)");
    expect(scope.page.forgotPassword.getCssValue("text-decoration")).toEqual("none");

    browser.actions().mouseMove(scope.page.forgotPassword).perform();

    expect(scope.page.forgotPassword.getCssValue("color")).toEqual("rgba(42, 100, 150, 1)");
    expect(scope.page.forgotPassword.getCssValue("text-decoration")).toEqual("underline");
});

The problem is that in about 1 out of 10 runs, it is failing with the following error messages:

Expected 'rgba(11, 51, 60, 1)' to equal 'rgba(42, 100, 150, 1)'.

Expected 'none' to equal 'underline'.

I suspect that it reads the css styles before they actually change.

What can I do to make the test more reliable and stable? Would appreciate any hints.

2条回答
贼婆χ
2楼-- · 2019-02-11 02:07

Following @P.T.'s suggestion, I've ended up making a custom reusable "Expected Condition":

waitForCssValue = function (elementFinder, cssProperty, cssValue) {
    return function () {
        return elementFinder.getCssValue(cssProperty).then(function(actualValue) {
            return actualValue === cssValue;
        });
    };
};

Example usage:

browser.wait(waitForCssValue(scope.page.forgotPassword, "color", "rgba(42, 100, 150, 1)"), 1000);
browser.wait(waitForCssValue(scope.page.forgotPassword, "text-decoration", "underline"), 1000);
查看更多
Bombasti
3楼-- · 2019-02-11 02:13

This asynchrony in the CSS update seems like something that protractor/webdriver should be able to wait for. Is your app doing anything unusual to implement the CSS update on hover? Is it specifying an animation or update delay somehow?

That said, I think there can be times when protractor cannot know that an update may take some time, so I think you can write the test with a different approach. Instead of expecting the value to be what you want (and racing with the change in the browser), can you re-phrase the test as "wait-until-value-I-want-shows-up"? (The failure case is a little slower and uglier, but hopefully that is rare.)

Checking for the text-decoration to move to 'underline' seems simpler (and presumably both will change "at once", so you only need to wait for one and can then check the other?)

So remove:

expect(scope.page.forgotPassword.getCssValue("text-decoration")).toEqual("underline");

and use something like this untested code:

browser.wait(function() { 
 return scope.page.forgotPassword.getCssValue("text-decoration")).then(function(value) {
   return value === 'underline';
 });

(Or use the Expected Conditions infrastructure for this ?)

You should be able to hide some of the ugliness in a function:

function waitForValue(valPromise, expectedVal) {
   return browser.wait(function() {
      return valPromise.then(function(value) {
         return value === expectedValue;
      });
   });
}

// Now your test can contain:
waitForValue(scope.page.forgotPassword.getCssValue("text-decoration"), 'underline');
查看更多
登录 后发表回答