Cypress: cy.focused() inconsistent behavior

Created on 17 Oct 2017  路  17Comments  路  Source: cypress-io/cypress

  • Operating System: Mac OS X Sierra
  • Cypress Version: 1.0.2
  • Browser Version: All

Is this a Feature or Bug?

Bug

Current behavior:

cy.focused() does the right thing when you keep the Cypress UI window focused. It does the wrong thing if you blur the Cypress UI window or if you run headlessly.

Desired behavior:

cy.focused() should produce consistent results, regardless of being run headed/headless or focused/blurred.

How to reproduce:

https://github.com/verheyenkoen/cypress-focused-issue

pkdriver bug

Most helpful comment

Hey, while this works in headless mode, focused() still fails when using the ui and not having the cypress window focused.
It would be great if it worked even when I'm on another tab or in my IDE, etc.

All 17 comments

I'm curious if chai-jQuery's newer .should.have.focus() would highlight some of the issues with our cy.focused() command when run headlessly. https://github.com/chaijs/chai-jquery/pull/73

We don't use chai-jquery anymore, nor did we ever support have.focus. Under the hood Cypress tries to normalize the focus behavior of a browser and for that we have to control the API's which return the focusable element. We store them internally as opposed to use the native DOM API's for this.

Can you think of any workarounds?

We don't use chai-jquery anymore

@brian-mann meaning these docs are out of date, and you implement the chai-jquery natively (via the string args in .should())?

I also second the OP -- currently cy.focused() yields window object (in some(?) cases) ).

IMO cy.focused() should correctly yield focused element.

Meanwhile, it can be done like this:

// get focused element
cy.get(":focus")

// assert if yielded element has focus
cy.get( selector ).should( $elem => {
    expect( $elem.is(":focus") ).to.be.true;
})

We technically do not use chai-jquery anymore. We rewrote the API and fixed a lot of things - but it behaves almost identically to what is currently documented on chai-jquery. We haven't updated the docs because its debatable what the docs should actually document.

Here's the code:
https://github.com/cypress-io/cypress/blob/develop/packages/driver/src/cypress/chai_jquery.coffee

The problem with focus in general is that it behaves differently whether or not the:

  • browser is actually in focus
  • the actual window of the frame is in focus
  • or the dev tools are open

Because of this we normalized the focus behavior (or at least attempted to) with cy.focused(). When you call cy.focus() on an element that does not receive focus due to the above listed reasons - we not only normalize the event and fire all of the appropriate events but we keep track of the state of the 'focused' element internally. That enables us to then fire the proper 'blur' events when the element should have lost focus.

What we do is basically a polyfill for when the browser doesn't do the "right thing" just because its out of focus.

I believe there was a specific technical reason we chose not to implement have.focus in the chai-jquery rewrite, but I can't remember what it was.

The problem is that because we are normalizing focus it means Cypress is the only source of truth to understand what should be in focus even when its not. Because of that - the have.focus assertion would have to call into the Cypress machinery and could not use anything else. It would basically just be the same thing as the yielded result from cy.focused().

We could override jquery's :is the same way we do visible + hidden to call into these internal functions. I guess that would work but I'd have to play with it to see if I'm missing edge cases. I want to say that every time we went down this path we couldn't figure out a way to consistently get it to work.

I ran into this issue when testing moving between text boxes in a form.
Basically was testing the behavior of focusing on the first text box, typed something, jumped to next one and typing something else.

@verheyenkoen Did you find any workaround for this?

I'm also running into this using [email protected]. Tests with .focused() will consistently fail when the Cypress UI is blurred, and will consistently succeed otherwise. The use case is similar to @bugzpodder's: filling a form (security number field) where focus automatically goes to the next field.

Sample test case:


cy
  .get('input[name="digit-1"]')
  .type('1')
  .should('have.value', '1')
  .focused()
  .should('have.attr', 'name', 'digit-2')

  .focused()
  .type('2')
  .should('have.value', '2')

  .focused()
  .type('3')
  .should('have.value', '3')
  // ...

I think I now tested for the existence of the autofocus attribute, which is not a consistent workaround obviously.

Running into this issue as well. I'm testing a listbox component. Tests are passing when I run Cypress through the GUI, but fails in headless mode.

Cypress test:

it('when listbox does not have option selected and receives focus, focus is delegated to the first option', function() {
    cy.get('#listbox--static .listbox__options').focus();
    cy.focused().then($el => {
      expect($el[0].className).to.equal('listbox__option');
      expect($el[0].innerText).to.contain('Option 1');
    })
  });

The feature is working and test is passing in GUI mode. But fails in headless Electron mode.

This issue was probably fixed in 3.0.2 via #1962 , I cannot reproduce the focus issue. cy.focused() should be consistent with document.activeElement

I'm closing this for now, but leave a comment if something still seems broken

I cannot confirm this fix. I upgraded my test repo to Cypress 3.0.2 and my test still fails. Please reopen.

@verheyenkoen I looked at your test repo. Can you explain what behavior you expect? Looking at the event handler you added:

document.getElementById('field').onkeyup = function() {

window.setTimeout(function() {

document.getElementById('field').blur();

}, 100)

}


I don't see why the element would be focused after typing into '#field'

Damn, what was I thinking?? Nevermind all that. Sorry for bothering...

Hey, while this works in headless mode, focused() still fails when using the ui and not having the cypress window focused.
It would be great if it worked even when I'm on another tab or in my IDE, etc.

@henrykuzmick what version of cypress are you on? cy.focused() should yield the same as document.activeElement or null if the body is focused, which will be unaffected by window focus

Was this page helpful?
0 / 5 - 0 ratings

Related issues

scottcrowe picture scottcrowe  路  3Comments

rbung picture rbung  路  3Comments

tahayk picture tahayk  路  3Comments

szabyg picture szabyg  路  3Comments

jennifer-shehane picture jennifer-shehane  路  3Comments