Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Use innerText rather than textContent for label queries. #190

Closed
wants to merge 4 commits into from

Conversation

ismail-codar
Copy link

@ismail-codar ismail-codar commented Jan 9, 2019

What:
Using label.innerText rather than label.textContent

Why:
An example dom:

<label>
Options
<select>
    <option>Option 1</option>
    <option>Option 2</option>
</select>
</label>

textContent returns incorrect result for label text

Options

    Option 1
    Option 2

but innerText returns correct result "Options".

But I can not be simulate with jest tests also label.innerText || label.textContent used because jsdom does not support innerText jsdom/jsdom#1245

How:

Checklist:

  • Documentation
  • Tests
  • Ready to be merged
  • Added myself to contributors table

label.textContent usage is problematic for select box in label
@kentcdodds
Copy link
Member

Interesting. I've never seen a select inside a label like that... 🤔

I remember writing this code and I remember researching and making the decision to use textContent rather than innerText. In retrospect I wish I had written a comment about that decision, but I didn't. I also didn't write a test to illustrate the issue that I saw with using innerText. I worry that if we merge this we'll break a bunch of tests 😬

@ismail-codar
Copy link
Author

ismail-codar commented Jan 10, 2019

  • Currently (using textContent) -> select inside label gives false results. It explained above.
  • Using innerText rather than textContent is correct solution but a lot of test are break because test runner (jest) using jsdom and jsom does not support innerText.
  • label.innerText || label.textContent usage is intermediate solution.

Also migrating jest to karma is another stuff to avoid jsdom related problems.

@kentcdodds
Copy link
Member

Ah, the fact that jsdom doesn't support innerText anyway may be the reason I landed with textContent actually 😅

I'm good with this PR 👍 Thanks for explaining!

src/queries.js Outdated
@@ -20,7 +20,7 @@ function queryAllLabelsByText(
const matcher = exact ? matches : fuzzyMatches
const matchNormalizer = makeNormalizer({collapseWhitespace, trim, normalizer})
return Array.from(container.querySelectorAll('label')).filter(label =>
matcher(label.textContent, label, text, matchNormalizer),
matcher(label.innerText || label.textContent, label, text, matchNormalizer),
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actually, I think it may be better to:

  1. Change this to: matcher(getNodeText(label), label, text, matchNormalizer),
  2. Update getNodeText to try innerText before textContent

This way we get the benefit of using innerText for all text selectors.

Could you make that adjustment here please?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I will sent it with a test.

@alexkrolick
Copy link
Collaborator

alexkrolick commented Jan 10, 2019

Does this still work if you have a nested span or something inside the label?

<label><span>Translation framework added a span</span></label>

I think we need to minimally add tests in JSDOM that mock out and test the different behavior.

@kentcdodds
Copy link
Member

Does this still work if you have a nested span or something inside the label?

image

Yep 👍

I think we need to minimally add tests in JSDOM that mock out and test the different behavior.

I think I agree with you, though I'm not sure what you're suggesting.

@alexkrolick
Copy link
Collaborator

I think I agree with you, though I'm not sure what you're suggesting.

Something like a stubbing out the .innerText method in the environment or at least for one test: jsdom/jsdom#1245 (comment)

Then verifying that form elements (or whatever else is excluded from innerTextification) are omitted.

? Due to the stubbing, this is probably more like a documentation case than something that is actually testing functionality. I guess you could just assert .innerText is called if present.

@kentcdodds
Copy link
Member

I guess you could just assert .innerText is called if present.

That's probably the best we could do by way of a test and I'm not sure how valuable that test would be but I'd definitely appreciate having it if @ismail-codar wants to add it.

getNodeText(label) is results to label.innerText
@@ -20,7 +20,7 @@ function queryAllLabelsByText(
const matcher = exact ? matches : fuzzyMatches
const matchNormalizer = makeNormalizer({collapseWhitespace, trim, normalizer})
return Array.from(container.querySelectorAll('label')).filter(label =>
matcher(label.textContent, label, text, matchNormalizer),
matcher(getNodeText(label), label, text, matchNormalizer),
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Don't forget that you'll need to update the getNodeText function to use innerText 👍


// this fails because getNodeText function returns incorrect result on JSDOM but it good for browser
// const element2 = getByLabelText(container, 'States');
// expect(element2.tagName).toBe('SELECT');
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This lines added temporarily added until switching jest to karma.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This lines added temporarily added until switching jest to karma.

Wait.... Are you attempting to migrate our tests to karma? If so then please don't take the time, it will not be merged.

If you want to add a few tests in karma and leave the existing tests as they are that's fine. The bill of the tests should be in jest, but I'd be happy to have a few tests that run in a real browser with karma.

Just want to make sure we're clear on expectations here.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok I understand.

@itsravenous
Copy link

What needs to happen to get this merged? I would love to be able to use getByText to select an element whose text is split within several child nodes.

@alexkrolick
Copy link
Collaborator

alexkrolick commented Feb 20, 2019

What needs to happen to get this merged? I would love to be able to use getByText to select an element whose text is split within several child nodes.

Not sure. It sounds like there a ton of layout edge cases causing innerText not to be practical in JSDOM. I don't know if working around/semi-polyfilling the behavior is a good idea if it deviates from the spec.

And naively doing a recursive join of the text content of all the nodes would be pretty memory-intensive and probably not what you want most of the time... 🤔

<body>
	<button>
		<span>Click Me</span>
	</button>
</body>

What gets returned here? Outermost element is body...?

Cypress uses the native innerText and textContent methods: https://github.com/cypress-io/cypress/blob/55b352a11d0a664517ae054c67432d98abc2389e/packages/driver/src/cy/commands/querying.coffee#L330-L337

@itsravenous-sky
Copy link

I think I misunderstood the purpose of this PR. I'm after being able to query for an element with text split over multiple children, e.g:

<div>Hi <span>there</span></div>

getByText('Hi there')

I'll look into it and raise an issue/PR

@kentcdodds
Copy link
Member

Ok, so in investigating this, I realized there's a flaw in the original assumption:

but innerText returns correct result "Options".

That is not correct. This is what I've learned:

labelNode.innerText
"Options 
Option 1
Option 2"


labelNode.textContent
"
Options

    Option 1
    Option 2
"

So, our current implementation is great because when running this code with getNodeText (without changes), we have:

getNodeText(labelNode)
"
Options

"

Which is correct I believe. So I'll go ahead and make that fix and we should be good to go.

@kentcdodds
Copy link
Member

🎉 This issue has been resolved in version 3.18.1 🎉

The release is available on:

Your semantic-release bot 📦🚀

@mcmillion
Copy link

Is this change why I suddenly have failing tests with getByLabelText for labels with nested spans? Just started happening today on a new machine with a fresh yarn install.

@kentcdodds
Copy link
Member

Ah yes, it might be. I think there's a solution to this. I'll have to work on it tomorrow. Could you make a new issue?

@NehrDani
Copy link

Ah yes, it might be. I think there's a solution to this. I'll have to work on it tomorrow. Could you make a new issue?

I did: #230

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
help wanted Extra attention is needed released
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

7 participants