Description
Describe the feature you'd like:
I've seen churn on what "text" means for a given element. The pendulum has swung from innerText to textContent based off unexpected use-cases. I think we should create a specification for what getNodeText
returns for the queries that rely on it.
Effected queries:
- *ByTitle
- *ByText
- *ByDisplayValue
I think *ByLabelText used to use it, but it is no longer.
Some examples that aren't working as expected.
Nested inline elements
This example is probably more common for Cypress, Puppeteer, etc. For example, Cypress implicitly waits for actionability. If a button is disabled
and the test calls cy.findByText('Hello').click()
, Cypress will not wait for the disabled
to be removed from the button
element. There is no workaround other than not using findByText
at all. Adding a selector
should define what element we want to return, but getNodeText
won't be able to find anything.
<button><span>Hello</span></button>
<script>
getByText('Hello') // returns span
getByText('Hello', { selector: 'button' }) // error - not found
// expected: `button` element
</script>
<th>
Country
<!-- Country Filter for the table header -->
<select>
<option>Colombia</option>
</select>
</th>
<script>
getByText('Country') // returns th
// expected: `th` element
</script>
Inconsistencies with *ByLabelText
<label for="1234">My Label<abbr>*</abbr></label>
<input id="1234">
<script>
getByText('My Label'); // returns <label>
getByLabelText('My Label'); // Error: Unable to find a label with the text of: My Label
getByText('My Label*'); // Error: Unable to find an element with the text: My Label*. This could be because the text is broken up by multiple elements. In ...
getByLabelText('My Label*'); // returns <input>
</script>
There are probably other edge cases we can build a specification around.
This would be a breaking change for existing implementations that rely on the current functionality. For example:
<button><span>Hello</span></button>
<script>
getByText('Hello') // error, 2 elements found. Before, only span was returned
getAllByText('Hello') // [button, span]. Before, only [span] was returned
getByText('Hello', { selector: 'span' }) // span
getByText('Hello', { selector: 'button' }) // button
</script>
Suggested implementation:
Write specifications for what should be returned given a few different DOM structures.
Describe alternatives you've considered:
I've looked through some of the history around the implementation of the getNodeText
function. It seems edge cases have driven the implementation, but there isn't much conditional coding to handle multiple edge cases at once.
Teachability, Documentation, Adoption, Migration Strategy:
Hopefully this feature doesn't require much documentation as it will mostly do what one would expect. Edge cases could be enumerated in the documentation, however. Something like:
getByText tries to grab elements by most likely text of an element. For example, a
button
element with nestedspan
elements will return the whole text of the button, spans included. getByText also tries to filter out unintended text. For example, a table header that contains aselect
element will remove the text of theselect
element itself as that is most likely the intent of selecting the table's header text.