Search code examples
javascriptcypressshadow-dom

Cypress - How to iterate over elements and filter out ones which have a shadow dom?


I have a page with many elements, five of which are div elements with zero attributes. Each of those five divs have a shadow root/shadow dom. I am interested in only one of the five divs which has an element with specific text. I am unable to find and filter out the correct div. How do I do that ? I have some code which works only when I have one such div instead of five.

cy.get("div").find(
   'div.items span:contains("7")',
   {
     includeShadowDom: true,
   }
).click();

Solution

  • I think you might be specifying the div#shadowroot twice - once in the get and once in the find.

    If I construct a similar page using Cypress' <cy-test-element> which has shadowDOM children, I can find span:contains("7") without problem.

    Test page with five shadow elements

    <body>
      <cy-test-element content="1"></cy-test-element>
      <cy-test-element content="3"></cy-test-element>
      <cy-test-element content="5"></cy-test-element>
      <cy-test-element content="7"></cy-test-element>
      <cy-test-element content="9"></cy-test-element>
      
      <script type="text/javascript">
        if (window.customElements) {
          window.customElements.define('cy-test-element', class extends HTMLElement {
            constructor () {
              super()
    
              const root = this.attachShadow({ mode: 'open' })
              const content = this.getAttribute('content') || 'Shadow Content'
              const className = this.hasAttribute('innerClass') ? this.getAttribute('innerClass') : 'shadow-content'
              const rootAddition = this.hasAttribute('rootAddition') ? this.getAttribute('rootAddition') : 'shadow-content'
    
              root.innerHTML = `<div class="shadow-div"><span class="${className}">${content}</span><input /><slot></slot></div>${rootAddition}`
            }
          })
        }
    
        if (window.location.search.includes('wrap-qsa')) {
          const realQuerySelectorAll = document.querySelectorAll;
          document.querySelectorAll = function (...args) {
            return realQuerySelectorAll.apply(document, args);
          };
        }
      </script>
    
    </body>
    

    DOM of live page

    <cy-test-element innerclass="7" content="7">
      #shadow-root
      <div class="shadow-div">
        <span class="7">7</span>
        <input>
        <slot></slot>
      </div>
    </cy-test-element>
    

    Test

    cy.get('cy-test-element')
      .find('span:contains("7")', {includeShadowDom:true})  // passes