11

According to the How do I assert an element is focused? thread, you can check if an element is focused by switching to an activeElement() and assert this is the same element you've expected to have the focus:

expect(page.element.getAttribute('id')).toEqual(browser.driver.switchTo().activeElement().getAttribute('id'));

In my case, the currently focused element does not have an id attribute.

What should I do instead of checking an id?

Bonus question: Also, as you can see from my tries to solve it, it looks like I cannot expect/assert an element (or web element) as a complete object. Why?


I've tried:

expect(page.element).toEqual(browser.driver.switchTo().activeElement());

But is is failing with an error I cannot even understand - there is a huge traceback (it is about 10 minutes to scroll in the console), but no user-friendly error inside.

I've also tried to use getWebElement():

expect(page.element.getWebElement()).toEqual(browser.driver.switchTo().activeElement());

But this resulted into the following error:

Error: expect called with WebElement argument, expected a Promise. Did you mean to use .getText()?

Using the latest protractor development version.

Community
  • 1
  • 1
alecxe
  • 462,703
  • 120
  • 1,088
  • 1,195
  • what is your ``page.element``, where do you assign it? – Betty St Feb 25 '15 at 22:44
  • @BettySt I prefer not to speak about an actual element and think about it as an abstract assuming there are no attributes to rely on..but if you ask it is `` element, which I can, strictly speaking identify by the `name` attribute..but I'd like to avoid that.. – alecxe Feb 26 '15 at 00:13
  • hmm I don't really understand why you don't specify the element.. there is somehow an user action than THIS element should be focused? Maybe it's easier for us when you describe a use case..? – Betty St Feb 26 '15 at 08:17
  • @BettySt I'm just checking that an element is in focus when the page finished loading. I can check one of it's attributes or text, as you've suggested, instead of an `id`, but I'd like to stay on an element level and assert an element or webelement - this is getting very protractor specific. Hope you understand what I mean. I think we are getting pretty close to solving it with glepretre. Thank you for the participation! – alecxe Feb 26 '15 at 14:53

7 Answers7

9

In my answer I'm going to assume activeElem and pageElem are both protractor element finders, and are pointing to the same web element.

First to answer your question about why

expect(activeElem).toEqual(pageElem);

Gets into an infinite loop, it's because protractor patched jasmine's expect to resolve the promise before asserting, so that things like expect(activeElem.getText()).toEqual('text'); works without having to do

activeElem.getText().then(function(text) {
  expect(text).toEqual('text');
})

You could say, why not just resolve the promise once? But then there are nested promises.

So now you might be thinking this is an issue, but it really isn't because you would never compare two elementFinders in a real use case. Jasmine's toEqual does a reference check, and not a deep compare, so expect(activeElem).toEqual(pageElem), is just the same as a simple reference comparison: (activeElem === pageElem).toToTruthy(), and there's really no point doing that. (Note element(by.css('html')) === element(by.css('html')) is false because it's not the same reference.)

So, to answer the real question for this thread: how to see if two elementFinders have the same underlying webelements:

expect(activeElem.getId()).toEqual(pageElem.getId());
hankduan
  • 5,994
  • 1
  • 29
  • 43
3

It's weird that it's expecting a promise only and could not handle a webdriver element... I had the same HUGE stacktrace as you.

Anyway, would you accept this kind of solution: send a "dumb" promise with a nice comment to justify why you had to do that. It's more a workaround than a semantic solution I admit.

expect(page.element.getInnerHtml())
  .toEqual(browser.driver.switchTo().activeElement().getInnerHtml());

It's working for me ;)

EDIT: Bonus answer

The reason you can't call expect with a WebElement comes from the Webdriver Control Flow Principle (I'm sure you already know about) and this line in jasminewd, the adapter for jasmine to Webdriver developped and used by Protractor ;)

glepretre
  • 8,154
  • 5
  • 43
  • 57
  • Yeah, this is certainly an interesting workaround! (out of upvotes for today - will do it tomorrow). Thank you for looking into it! – alecxe Feb 25 '15 at 18:33
  • @alecxe more than the upvote, let me know if it works :) you're welcome! – glepretre Feb 25 '15 at 18:48
  • @alecxe thanks! I edited this morning to try answering your bonus question, if you want to take a look ;) – glepretre Feb 26 '15 at 11:20
  • Thank you for the update on the bonus question. Yeah, this is certainly jasminewd's expect specific issue. What do you think is the reason for having an `actual instanceof webdriver.WebElement` check? They even have a [test](https://github.com/angular/jasminewd/blob/b77f440a3af6d7830e18f63ae5dbff4cbf80c14f/spec/adapterSpec.js#L170) for it. – alecxe Feb 26 '15 at 14:58
  • Btw, do you think getting `outerHTML` instead of `innerHTML` would be better? – alecxe Feb 26 '15 at 15:01
  • @alecxe Of course they have a test for it! :) maybe you should open an issue in jasminewd. And we could also ask jmr or hankduan – glepretre Feb 26 '15 at 16:08
  • @alecxe yes, outerHtml might be more reliable if the element is not specific enough but I don't know in which testing context you are. There are few chances to have fake positive I think. – glepretre Feb 26 '15 at 16:59
  • 1
    I think hankduan described the problem perfectly, check it out. Thanks again for your help! – alecxe Feb 26 '15 at 21:04
  • I cannot understand in what world your answer deserves a downvote. – alecxe Feb 27 '15 at 14:33
  • 1
    @glepretre how could we do this now that protractor [removed the `getInnerHtml` method?](https://github.com/angular/protractor/blob/master/CHANGELOG.md) – eLRuLL Mar 18 '17 at 03:50
  • @eLRuLL That's an interesting question! We haven't upgraded protractor on projects using this snippet. I guess in this specific case you could use another still supported method. If you find the answer, feel free to edit my post :) – glepretre Aug 18 '17 at 13:24
2

For the future readers, I've created a custom matcher to assert if an element is active/focused:

beforeEach(function() {
    jasmine.addMatchers({
        toBeActive: function() {
            return {
                compare: function(elm) {
                    return {
                        pass: elm.getId().then(function (activeElementID) {
                            return browser.driver.switchTo().activeElement().getId().then(function (currentElementID) {
                                return jasmine.matchersUtil.equals(currentElementID, activeElementID);
                            });
                        })
                    };
                }
            };
        }
    });
});

Usage:

expect(element(by.css("#myid")).toBeActive();
alecxe
  • 462,703
  • 120
  • 1,088
  • 1,195
1

What about using CSS selectors and :focus

expect(element.all(by.css('#myid:focus')).count()).toBe(1);
Tunaki
  • 132,869
  • 46
  • 340
  • 423
1

I solved it by using protractor's: ElementFinder.prototype.equals

const activeElement = await browser.driver.switchTo().activeElement();
const potentialyFocusedElement = await component.$('your-locator');
const isFocused = await potentialyFocusedElement.equals(activeElement);

I am not sure how exactly equals work, if it does a deep compare, or it somehow compares instances. But it works.

Note that you can not call activeElement.equals(...) since it is not an ElementFinder, it is a WebElement.

0

You need to come up with a way to tell Protractor/webdriver how to find your element on the page. Protractor uses JavaScript to find elements, so any of the tools available for inspecting the DOM are available. Protractor and webdriver wrap these APIs in the various by flavors:

http://angular.github.io/protractor/#/api?view=ProtractorBy

In addition to the basic flavors, Protractor adds Angular-aware flavors (so you can search by Angular binding, etc).

If your element does not have any distinguishing characteristics, then it might be worth adding something to make it easier to test. Or, (though this is often frowned upon because its so fragile), you can use an xpath. See https://github.com/angular/protractor/blob/master/docs/locators.md

P.T.
  • 24,557
  • 7
  • 64
  • 95
  • That's a perfectly valid point. I am still not sure why I cannot assert an element (or web element) itself as an object, but have to check one of it's attributes instead..I'll open up a bounty and keep this question open for a while. Thank you! – alecxe Feb 25 '15 at 13:20
  • Ah, I think I mis-read your question. Can you include the definition of "page.element"? I think that will make things clearer. – P.T. Feb 25 '15 at 18:31
  • Sorry for being unclear. I would like to think about `page.element` as an abstract element which, for the sake of the topic, cannot be reliably and uniquely identified with an attribute. Anyway, `page.element` refers to an input element: ``. In practice, yeah, I can identify it by name, but now I'm more focused on why do we need to go down to element attributes instead of just checking if an active element is the one we've already found - staying on the element's level. Hope you understand what I mean. Anyway, your point is still valid and I want it to be here. Thanks! – alecxe Feb 25 '15 at 18:40
0

You can simply check the text (or anything else) of the element:

var currentElement = browser.switchTo().activeElement();
expect(currentElement.getText()).toEqual(page.element.getText());
Betty St
  • 2,741
  • 21
  • 33
  • This is close to what @P.T. suggested and the point is perfectly valid. Right now I'm more focused on the bonus question - in other words - why I cannot check the complete element and have to go down to attributes or text. Thank you for participation! – alecxe Feb 26 '15 at 00:12