cypress-io / cypress

Fast, easy and reliable testing for anything that runs in a browser.
https://cypress.io
MIT License
46.69k stars 3.16k forks source link

Difficulty Typing in Cypress Tests for Custom Elements with Shadow DOM #29118

Open nnaydenow opened 6 months ago

nnaydenow commented 6 months ago

Current behavior

Use Case

I'm attempting to test my custom elements with Cypress. I understand the limitation that makes it challenging to determine if the custom component is typable or focusable. Therefore, my custom elements provide getFocusDomRefAsync, which waits for the initial rendering of the custom element and then returns the appropriate DOM reference to execute actions.

<my-button>
    #shadow root
    <button>Some button</button> <!-- Result of getFocusDomRefAsync -->
</my-button>

I've attempted to add custom queries and commands to type into the correct DOM reference in the following ways:

  1. Using a Custom Command:

    Cypress.Commands.add("typeInCorrectElement", { prevSubject: 'element' }, (subject, text, options = {}) => {
    cy.get(subject)
        .then(async $subject => {
            const $realDomRef = await $subject.get(0).getFocusDomRefAsync()
    
            console.log("======", $realDomRef)
            cy.wrap($realDomRef).type(text, options)
        })
    })
  2. Adding a Custom Query:

    
    Cypress.Commands.add("typeInCorrectElement2", { prevSubject: 'element' }, (subject, text, options = {}) => {
    cy.wrap(subject)
        .getFocusDomRef()
        .then($realDomRef => {
    
            console.log("======", $realDomRef)
            cy.wrap($realDomRef).type(text, options)
        })
    })

Cypress.Commands.addQuery('getFocusDomRef', function () { return async (subject) => { if (!subject?.[0].tagName.startsWith('UI5')) { const err = getFocusDomRef() needs to be chained to; throw new TypeError(err); } const shadow = await subject[0].getFocusDomRefAsync(); return Cypress.dom.wrap(shadow) }; });


3. **Overwriting Focus Command and Focused Query:**
   Overwriting the focus command and focused query to return the exact active element, regardless of whether it's inside a shadow root or not.

### Expected Usage

I'm seeking suggestions on what else I could try, what I might be doing incorrectly, or if there's any documentation or guides available on how to achieve this. I have over 20 actionable components, making it impractical to add attributes directly to the custom element and traverse every single shadow DOM, especially since we already have handling in the library via the `getFocusDomRefAsync` method.

### Desired behavior

```javascript
cy.get("#button1")
    .typeInCorrectElement("{enter}")

I'm seeking suggestions on what else I could try, what I might be doing incorrectly, or if there's any documentation or guides available on how to achieve this. I have over 20 actionable components, making it impractical to add attributes directly to the custom element and traverse every single shadow DOM, especially since we already have handling in the library via the getFocusDomRefAsync method.

Test code to reproduce

Repo: https://github.com/nnaydenow/cypress-ce

https://github.com/cypress-io/cypress/assets/31909318/154f9ee2-88d0-4e8f-94a9-701bdf1c1848

Cypress Version

13.6.4

Node version

v20.11.0

Operating System

14.3.1

Debug Logs

No response

Other

No response

jennifer-shehane commented 6 months ago

I'm a little confused about which element you're trying to target to type into from your example code.

If you're dealing with Shadow DOM, you'll have to use .shadow() to query the elements correctly and I didn't see you using that. Have you looked at this command? https://docs.cypress.io/api/commands/shadow

nnaydenow commented 6 months ago

Hi @jennifer-shehane,

I'm trying to type inside button which is placed inside the shadow root of my component which is:

<my-button>
    #shadow root
    <button>Some button</button> <=== this one
</my-button>

Do I have to use the .shadow if I already have the DOM element in which I want to type? If no, then myButton.getFocusDomRefAsync() will return a valid element in which I expect to be able to write.

In my understanding I need to use .shadow() only if I need to find the element, not if I already have it. Is it correct?

cypress-app-bot commented 1 week ago

This issue has not had any activity in 180 days. Cypress evolves quickly and the reported behavior should be tested on the latest version of Cypress to verify the behavior is still occurring. It will be closed in 14 days if no updates are provided.