0

I have a such HTML code.

<div id ='pages'>
    <div id='wrapper'>1 </div>
    <div id='wrapper'>2 </div>
</div>

I am want to find elements count with id wrapper. I using Cypress. I'm starting to learn Cypress. If I try:

cy.get('div#wrapper').should('have.length', 2)

I get AssertionError:

CypressError: Timed out retrying: expected 1 to equal 2
Narine Poghosyan
  • 853
  • 3
  • 16
  • 26
  • You can't have two elements with the same id in one page. – jonrsharpe Mar 20 '19 at 20:59
  • I am tester, And developers using vue js, There are its possible. – Narine Poghosyan Mar 20 '19 at 21:01
  • 1
    *Shouldn't*, then; you need more competent developers, whichever framework they're using. It's strictly invalid HTML, and you shouldn't be surprised that the selector only finds one of them. See e.g. https://stackoverflow.com/questions/5611963/can-multiple-different-html-elements-have-the-same-id-if-theyre-different-eleme – jonrsharpe Mar 20 '19 at 21:05
  • Now I wanted found element counts, ex. let be classname with wrapper – Narine Poghosyan Mar 20 '19 at 21:08
  • This is answer for my question cy.get('#pages') .find('div#wrapper') .should(($div) => { expect($div).to.have.length(2) }) – Narine Poghosyan Mar 20 '19 at 22:32

1 Answers1

1

As jonrsharpe pointed out, it's invalid HTML to have multiple elements with identical id attribute.

That being said, DOM is quite smart and can recover and work even with invalid HTML. Duplicate-id elements shouldn't cause much trouble.

If you e.g. try doing document.querySelectorAll('#wrapper') it should return list of 2 elements (in your case).

Problem is, Cypress is using jQuery to query the DOM instead of using native DOM methods and I guess jQuery isn't as smart (or it's more pedantic).

That being said, I can't reproduce that error when running:

// succeeds
cy.get('div#wrapper').should('have.length', 2)

Only when querying #wrapper directly (without the preceding div):

// fails
cy.get('#wrapper').should('have.length', 2)

I reckon this is because jQuery uses a heuristic of exiting early when a selector string (#wrapper) contains only a single id (and that's why div#wrapper returns both elements).

Also, your solution in comments (cy.get('#pages') .find('div#wrapper') .should(($div) => { expect($div).to.have.length(2) })), while working, isn't ideal because it won't retry. Let me demonstrate:

In the following code, the 2nd #wrapper will appear in the DOM only after 1 sec.

describe( 'test', () => {
    beforeEach(() => {
        cy.document().then( doc => {
            doc.body.innerHTML = `
                <div id='pages'>
                    <div id='wrapper'>1</div>
                </div>
            `;
            setTimeout(() => {
                doc.body.innerHTML = `
                    <div id='pages'>
                        <div id='wrapper'>1</div>
                        <div id='wrapper'>2</div>
                    </div>
                `;
            }, 1000 );
        });
    });

    // will fail
    it('solution A', () => {
        cy.get('#pages') // <- won't be retried
            .find('div#wrapper') // <- only this command will be retried
            .should( $div => expect($div).to.have.length(2) );
    });

    // will pass
    it('solution B', () => {
        cy.get('#pages #wrapper') // <- will be retried and succeed in 1sec
            .should( $div => {
                expect($div).to.have.length(2);
            });
    });

    // will pass
    it('solution C', () => {
        cy.get('#pages')
            .should($pages => {
                // using native DOM querying
                expect($pages[0].querySelectorAll('#wrapper').length).to.eq(2);
            });
    });
});

Thus, you should go with solution similar to B or C.

dwelle
  • 6,894
  • 2
  • 46
  • 70
  • I started learning cypress and another question is here. So we can have multiple components with same classNames in the same screen for styling purpose. In this case, how can we find one of them to test? add IDs to all of those components? – Kid Feb 14 '22 at 16:14