113

I'm trying to test if an element is visible using protractor. Here's what the element looks like:

<i class="icon-spinner icon-spin ng-hide" ng-show="saving"></i>

When in the chrome console, I can use this jQuery selector to test if the element is visible:

$('[ng-show=saving].icon-spin')
[
<i class=​"icon-spinner icon-spin ng-hide" ng-show=​"saving">​</i>​
]
> $('[ng-show=saving].icon-spin:visible')
[]

However, when I try to do the same in protractor, I get this error at runtime:

InvalidElementStateError: 
invalid element state: Failed to execute 'querySelectorAll' on 'Document': 
'[ng-show=saving].icon-spin:visible' is not a valid selector.

Why is this not valid? How can I check for visibility using protractor?

limp_chimp
  • 13,475
  • 17
  • 66
  • 105
  • @limp_chimp for such things as visibility, think about using AngularJS client DOM unit tests. They are much faster to run and easier to develop. – Offirmo Sep 24 '14 at 15:37

11 Answers11

147

This should do it:

expect($('[ng-show=saving].icon-spin').isDisplayed()).toBe(true);

Remember protractor's $ isn't jQuery and :visible is not yet a part of available CSS selectors + pseudo-selectors

More info at https://stackoverflow.com/a/13388700/511069

Leo Gallucci
  • 16,355
  • 12
  • 77
  • 110
  • 2
    The answer below also uses `isDisplayed()` but just expands to resolve the promise for completeness though that step is optional and only meant for including conditionals in your tests which is a bad practice. @asenovm can you further elaborate your "This is plain wrong" comment? – Leo Gallucci Aug 09 '16 at 11:54
  • @LeoGallucci, isDisplayed() returns ElementFinder and not a boolean. – asenovm Aug 09 '16 at 12:13
  • 2
    Please don't use `.toBeTruthy();` use `.toBe(true)` instead. `.toBeTruthy();` will match things like [], 'false', 42. Basically anything expect 0, "", null, undefined, NaN or false is truthy. – Brian May 21 '19 at 09:10
78

The correct way for checking the visibility of an element with Protractor is to call the isDisplayed method. You should be careful though since isDisplayed does not return a boolean, but rather a promise providing the evaluated visibility. I've seen lots of code examples that use this method wrongly and therefore don't evaluate its actual visibility.

Example for getting the visibility of an element:

element(by.className('your-class-name')).isDisplayed().then(function (isVisible) {
    if (isVisible) {
        // element is visible
    } else {
        // element is not visible
    }
});

However, you don't need this if you are just checking the visibility of the element (as opposed to getting it) because protractor patches Jasmine expect() so it always waits for promises to be resolved. See github.com/angular/jasminewd

So you can just do:

expect(element(by.className('your-class-name')).isDisplayed()).toBeTruthy();

Since you're using AngularJS to control the visibility of that element, you could also check its class attribute for ng-hide like this:

var spinner = element.by.css('i.icon-spin');
expect(spinner.getAttribute('class')).not.toMatch('ng-hide'); // expect element to be visible
pcatre
  • 1,304
  • 2
  • 16
  • 24
Mobiletainment
  • 22,201
  • 9
  • 82
  • 98
8

I had a similar issue, in that I only wanted return elements that were visible in a page object. I found that I'm able to use the css :not. In the case of this issue, this should do you...

expect($('i.icon-spinner:not(.ng-hide)').isDisplayed()).toBeTruthy();

In the context of a page object, you can get ONLY those elements that are visible in this way as well. Eg. given a page with multiple items, where only some are visible, you can use:

this.visibileIcons = $$('i.icon:not(.ng-hide)'); 

This will return you all visible i.icons

Brine
  • 3,733
  • 1
  • 21
  • 38
5

If there are multiple elements in DOM with same class name. But only one of element is visible.

element.all(by.css('.text-input-input')).filter(function(ele){
        return ele.isDisplayed();
    }).then(function(filteredElement){
        filteredElement[0].click();
    });

In this example filter takes a collection of elements and returns a single visible element using isDisplayed().

A Qadeer Qureshi
  • 325
  • 7
  • 12
  • This is a great answer; consider the case where there is no such element! $('.text-input-input') would alert the user elegantly; this might fail because `filteredElement.length === 0`? – Nate Anderson Jan 04 '18 at 02:58
1

This answer will be robust enough to work for elements that aren't on the page, therefore failing gracefully (not throwing an exception) if the selector failed to find the element.

const nameSelector = '[data-automation="name-input"]';
const nameInputIsDisplayed = () => {
    return $$(nameSelector).count()
        .then(count => count !== 0)
}
it('should be displayed', () => {
    nameInputIsDisplayed().then(isDisplayed => {
        expect(isDisplayed).toBeTruthy()
    })
})
activedecay
  • 10,129
  • 5
  • 47
  • 71
1

To wait for visibility

const EC = protractor.ExpectedConditions;
browser.wait(EC.visibilityOf(element(by.css('.icon-spinner icon-spin ng-hide')))).then(function() {
  //do stuff
})

Xpath trick to only find visible elements

element(by.xpath('//i[not(contains(@style,"display:none")) and @class="icon-spinner icon-spin ng-hide"]))
Drew Royster
  • 120
  • 12
1
element(by.className('your-class-name'))
  .isDisplayed()
  .then(function (isVisible) {
     if (isVisible) { // element is visible
     } else {         // element is not visible
     }
  })
  .catch(function(err){
     console.error("Element is not found! ", err);
  })
Mete Korucu
  • 113
  • 12
Anil Kumar
  • 61
  • 4
0

Here are the few code snippet which can be used for framework which use Typescript, protractor, jasmine

browser.wait(until.visibilityOf(OversightAutomationOR.lblContentModal), 3000, "Modal text is present");

// Asserting a text

OversightAutomationOR.lblContentModal.getText().then(text => {
                    this.assertEquals(text.toString().trim(), AdminPanelData.lblContentModal);
                });

// Asserting an element

expect(OnboardingFormsOR.masterFormActionCloneBtn.isDisplayed()).to.eventually.equal(true

    );

OnboardingFormsOR.customFormActionViewBtn.isDisplayed().then((isDisplayed) => {
                        expect(isDisplayed).to.equal(true);
                });

// Asserting a form

formInfoSection.getText().then((text) => {
                        const vendorInformationCount = text[0].split("\n");
                        let found = false;
                        for (let i = 0; i < vendorInformationCount.length; i++) {
                            if (vendorInformationCount[i] === customLabel) {
                                found = true;
                            };
                        };
                        expect(found).to.equal(true);
                    });     
Khyati Sehgal
  • 375
  • 1
  • 6
  • 21
0

Something to consider

.isDisplayed() assumes the element is present (exists in the DOM)

so if you do

expect($('[ng-show=saving]').isDisplayed()).toBe(true);

but the element is not present, then instead of graceful failed expectation, $('[ng-show=saving]').isDisplayed() will throw an error causing the rest of it block not executed

Solution

If you assume, the element you're checking may not be present for any reason on the page, then go with a safe way below

/**
*  element is Present and is Displayed
*  @param    {ElementFinder}      $element       Locator of element
*  @return   {boolean}
*/
let isDisplayed = function ($element) {
  return (await $element.isPresent()) && (await $element.isDisplayed())
}

and use

expect(await isDisplayed( $('[ng-show=saving]') )).toBe(true);
Sergey Pleshakov
  • 7,964
  • 2
  • 17
  • 40
0
waitTillElementIsPresent(locator: Locator): promise.Promise<boolean> 
{

const EC = protractor.ExpectedConditions;
return browser.wait(EC.visibilityOf(element(by.id('xyz')), browser.params.explicitWaitTime, 'Element taking too long to appear in the DOM');

}
Adrian Mole
  • 49,934
  • 160
  • 51
  • 83
Ketan Pardeshi
  • 516
  • 1
  • 5
  • 8
  • 2
    Your answer could be improved with additional supporting information. Please [edit] to add further details, such as citations or documentation, so that others can confirm that your answer is correct. You can find more information on how to write good answers [in the help center](/help/how-to-answer). – Community Oct 11 '21 at 11:05
0
const isDisplayed = await $('div').isDisplayed().then(null, err => false)
Xotabu4
  • 3,063
  • 17
  • 29