12

I am writing a Cypress test for a React project. I need to be able to target an <input> nested inside of a <label> so I can type into that input field. The <input>s have no class ids.

Here is my HTML.

    <label class="Input">
        <div class="label">LABEL TEXT</div>
        <input type="text">
    </label>

I have numerous inputs in the same form with the exact HTML shown above. They only differ by the text in the <div> (i.e. LABEL TEXT).

The reason that I am surrounding the <input> in a label tag is that I want the user to be able to target the input by clicking anywhere surrounding the label text OR the input. Adding a class to the inputs doesn't make sense for our code-base just for the sake of Cypress testing. I cannot use pseudo selectors such as :first cy.get('input[type="text"]:first') because I don't want my tests to break if I add additional inputs to the form.

I have tried the following, but it tries to type into the label instead of the input.

  cy.contains('LABEL TEXT')
      .click()
      .type('test')

Even though it puts focus on the in the Cypress test runner, it still tries to type into the <div> instead of the focused input.

As I am very new to Cypress and assertions, I am left scratching my head. I'm interested in a solution (if possible) that doesn't involve adding classes to my inputs just for the sake of Cypress testing. I'm hoping this is just a shortcoming in my CSS, Cypress, or assertion knowledge.

Thank you

Ray Rackiewicz
  • 303
  • 1
  • 3
  • 12

3 Answers3

12

I've just reproduced your exact same scenario of having several identical labels with just the inner div's texts being different. Answer of Miguel Carvajal was close but needed some tweaking. Following code worked properly:

cy.get("div[class='label']").contains("LABEL TEXT2").parent().within(() => {
   cy.get("input[type='text']").type("StackOverflowHelp")
})

Tested on following HTML:

<label class="Input">
  <div class="label">LABEL TEXT1</div>
  <input type="text">
</label>
<label class="Input">
  <div class="label">LABEL TEXT2</div>
  <input type="text">
</label>

Result:

enter image description here

DurkoMatko
  • 5,238
  • 4
  • 26
  • 35
5

I think something like

cy.getByText("label text").parent().within(() => {

   cy.get('input') // this yields the input
})

will work.

I recommend you to check out https://github.com/testing-library/cypress-testing-library for some neat selectors

Miguel Carvajal
  • 1,785
  • 19
  • 23
1

Yes, you can use CSS selectors to get some more precision!

Try:

cy.get('.Input > input') // selects an <input> tag that is a direct
                         // descendant of a "Input" class
.click()
.type('test')

You can also use CSS selectors to select the nth element in the DOM that matches a selector:

cy.get('.Input:nth-child(2) > input') // selects all <input> tags that
                                      // are direct children of the
                                      // second "Input"-class element
                                      // in any containing element
.click()
.type('test')
Zach Bloomquist
  • 5,309
  • 29
  • 44
  • Unfortunately, I have numerous inputs in the form ALL with the same HTML structure shown in my example. They only differ by the contents of the
    ; the text "LABEL TEXT". Therefore, Cypress with throw an error because it will not result in a single element targeted in the DOM. I'll clarify this in my question. Thanks.
    – Ray Rackiewicz Jul 03 '19 at 19:40
  • You can use CSS selectors to select the nth element in the DOM. :) I'll add it to my answer. – Zach Bloomquist Jul 08 '19 at 13:33