-
Notifications
You must be signed in to change notification settings - Fork 472
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
feat(custom normalizer): allow custom control of normalization #172
feat(custom normalizer): allow custom control of normalization #172
Conversation
Adds an optional {normalizer} option to query functions; this is a transformation function run over candidate match text after it has had `trim` or `collapseWhitespace` run on it, but before any matching text/function/regexp is tested against it. The use case is for tidying up DOM text (which may contain, for instance, invisible Unicode control characters) before running matching logic, keeping the matching logic and normalization logic separate.
src/__tests__/text-matchers.js
Outdated
@@ -194,3 +194,115 @@ cases( | |||
}, | |||
}, | |||
) | |||
|
|||
// A good use case for a custom normalizer is stripping | |||
// out UCC codes such as LRM before matching |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can we expand out acronyms like this for those who might be unfamiliar with Unicode (UCC -> Unicode Control Character)? 😄
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yep, I did expand it out in the user-facing docs, but I'll expand it here too.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks! I have some thoughts around this.
This commit moves the implementation of `trim` + `collapseWhitespace`, making them just another normalizer. It also exposes a `getDefaultNormalizer()` function which provides the default normalization and allows for the configuration of it. Removed `matches` and `fuzzyMatches` from being publicly exposed. Updated tests, added new documentation for normalizer and getDefaultNormalizer, and removed documentation for the previous top-level `trim` and `collapseWhitespace` options.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good with just about everything :)
a string and is expected to return a normalized version of that string. | ||
|
||
Note: Specifying a value for `normalizer` _replaces_ the built-in normalization, but | ||
you can call `getDefaultNormalizer` to obtain a built-in normalizer, either |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'd prefer to not allow people to call this function and instead simply export it:
export const normalizer = getDefaultNormalizer({trim: true, collapseWhitespace: true})
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ok, easy to do, but then somebody wouldn't be able to get hold of, say, a default normalizer that only trims but doesn't collapse whitespace.
If you're happy with that, that's fine, and I'll hide getDefaultNormalizer
and take all mention of trim
and collapseWhitespace
out of the docs.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
then somebody wouldn't be able to get hold of, say, a default normalizer that only trims but doesn't collapse whitespace.
I'm thinking that's a pretty minimal use-case, and if it is, they just need to copy/paste this:
function getDefaultNormalizer({trim = true, collapseWhitespace = true} = {}) {
return text => {
let normalizedText = text
normalizedText = trim ? normalizedText.trim() : normalizedText
normalizedText = collapseWhitespace
? normalizedText.replace(/\s+/g, ' ')
: normalizedText
return normalizedText
}
}
Considering we're getting simplicity in exchange for a tiny bit of flexibility for 1% of use cases I think that's a win :)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'll also have to remove the example for how to match text without trimming, for instance:
getByText(node, 'text', {normalizer: getDefaultNormalizer({trim: false})})
There'd be no easy way to do that without getDefaultNormalizer
: they'd have to implement their own whitespace-collapsing normalizer. I don't have much of an opinion on that, but presumably the existing trim
and collapseWhitespace
options were added because somebody wanted to be able to turn them off?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ah, that makes sense, considering I want to deprecate trim
as an option. Ok, let's stick with what you have. I'd like to have @alexkrolick give this a look over too, but I'm 👍
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think some of the queries initially had different defaults for trim and collapseWhitespace, and they had different enough effects to keep separate (see #31).
I'm on the fence about exporting the factory function, but I definitely don't want to encourage copy-pasting the default implementation - that would be opting-out of future changes.
I think this is good as-is and leaves room to come up with future more streamlined APIs for other use cases.
@@ -6,7 +6,7 @@ export * from './queries' | |||
export * from './wait' | |||
export * from './wait-for-element' | |||
export * from './wait-for-dom-change' | |||
export * from './matches' | |||
export {getDefaultNormalizer} from './matches' |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This would be a breaking change
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, as I commented in the commit, I removed matches
and fuzzyMatches
: they looked like they were exported accidentally, and aren't documented. Are they officially supported?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
They were used in jest-dom but it looks like that dependency was removed:
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
So... that means they still need to be exposed for old versions of jest-dom
that are bringing in new versions of dom-testing-library
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
(Actually, looking closer at those links, where were matches
and fuzzyMatches
used in jest-dom
? I can see getNodeText
, but at a quick skim I can't see matches
and fuzzyMatches
. It looks like they had their own implementation of matches
. Am I missing something?)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Was referring to this comment testing-library/jest-dom#9 (comment)
But you're right it doesn't look like it was used directly.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@kentcdodds can you confirm whether this is a breaking change? I'm happy to change the PR so matches
and fuzzyMatches
aren't changed, if necessary, but it'd be preferable not to continue to expose those internals or to have to keep the same APIs for them.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm fine with this change.
matcher, | ||
{collapseWhitespace = true, trim = true} = {}, | ||
) { | ||
function matches(textToMatch, node, matcher, normalizer) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is there a way we can add this option without making a breaking change to the matches APIs?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I guess we can ignore this if removing the matchers as exports per #172 (comment)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Temporary hold - there are breaking changes
I hope that you don't mind if we merge this one first: #169 😬 |
Let's wait for the new |
Sure. Will keep an eye on the |
I'm merging in the |
Rather than rebasing at this point I've pushed a merge from master and a small delta to apply normalization to queryByDisplayValue. That seems to keep existing review comments easier to follow, but I can push a squashed commit if you prefer? It's all there now. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Awesome 💯
🎉 This PR is included in version 3.15.0 🎉 The release is available on:
Your semantic-release bot 📦🚀 |
What:
(PR for issue #171)
Adds an optional {normalizer} option to query functions; this
is a transformation function run over candidate match text
after it has had
trim
orcollapseWhitespace
run on it,but before any matching text/function/regexp is tested
against it.
The use case is for tidying up DOM text (which may
contain, for instance, invisible Unicode control characters)
before running matching logic, keeping the matching logic
and normalization logic separate.
Why:
It's otherwise not easy to apply normalization logic to DOM text without having to replace large portions of the matching logic.
How:
Added a new optional
normalizer
function which applies aftertrim
+collapseWhitespace
but before matching logic.I had to make the same changes in several places in the code as there's a lot of similar option-handling code around, unfortunately. That also meant lots of test cases to ensure all those paths are tested.
Checklist: