From 2273f03300dbdb360e02e08f73fd053fe2e82c80 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ernesto=20Garc=C3=ADa?= Date: Wed, 11 Apr 2018 15:31:27 -0300 Subject: [PATCH] feat(getNodeText ): add getNodeText and ignore extra whitespace in node texts (#21) * Ignore extra whitespace in all contexts when matching text * Expose getText * Properly expose getText * Rename getText as getNodeText * Document getNodeText in the README * Typo --- README.md | 19 +++++++++++++++++++ src/get-node-text.js | 12 ++++++++++++ src/index.js | 1 + src/matches.js | 10 +++++++--- src/queries.js | 16 ++-------------- 5 files changed, 41 insertions(+), 17 deletions(-) create mode 100644 src/get-node-text.js diff --git a/README.md b/README.md index d2c35efd..c26dd328 100644 --- a/README.md +++ b/README.md @@ -395,6 +395,25 @@ fireEvent.click(getElementByText('Submit'), rightClick) // default `button` property for click events is set to `0` which is a left click. ``` +#### `getNodeText(node: HTMLElement)` + +Returns the complete text content of a html element, removing any extra +whitespace. The intention is to treat text in nodes exactly as how it is +perceived by users in a browser, where any extra whitespace within words in the +html code is not meaningful when the text is rendered. + +```javascript +//
+// Hello +// World ! +//
+const text = getNodeText(container.querySelector('div')) // "Hello World !" +``` + +This function is also used internally when querying nodes by their text content. +This enables functions like `getByText` and `queryByText` to work as expected, +finding elements in the DOM similarly to how users would do. + ## Custom Jest Matchers When using [jest][], we recommend that you import a set of custom matchers that diff --git a/src/get-node-text.js b/src/get-node-text.js new file mode 100644 index 00000000..dc1c8a67 --- /dev/null +++ b/src/get-node-text.js @@ -0,0 +1,12 @@ +function getNodeText(node) { + return Array.from(node.childNodes) + .filter( + child => child.nodeType === Node.TEXT_NODE && Boolean(child.textContent), + ) + .map(c => c.textContent) + .join(' ') + .trim() + .replace(/\s+/g, ' ') +} + +export {getNodeText} diff --git a/src/index.js b/src/index.js index f569d9d7..13584048 100644 --- a/src/index.js +++ b/src/index.js @@ -8,5 +8,6 @@ export * from './queries' export * from './wait' export * from './wait-for-element' export * from './matches' +export * from './get-node-text' export * from './events' export * from './bind-element-to-queries' diff --git a/src/matches.js b/src/matches.js index 3721ff32..7a8d64bb 100644 --- a/src/matches.js +++ b/src/matches.js @@ -2,12 +2,16 @@ function matches(textToMatch, node, matcher) { if (typeof textToMatch !== 'string') { return false } + const normalizedText = textToMatch + .toLowerCase() + .trim() + .replace(/\s+/g, ' ') if (typeof matcher === 'string') { - return textToMatch.toLowerCase().includes(matcher.toLowerCase()) + return normalizedText.includes(matcher.toLowerCase()) } else if (typeof matcher === 'function') { - return matcher(textToMatch, node) + return matcher(normalizedText, node) } else { - return matcher.test(textToMatch) + return matcher.test(normalizedText) } } diff --git a/src/queries.js b/src/queries.js index 0fca2995..add0d351 100644 --- a/src/queries.js +++ b/src/queries.js @@ -1,5 +1,6 @@ import prettyFormat from 'pretty-format' import {matches} from './matches' +import {getNodeText} from './get-node-text' const {DOMElement, DOMCollection} = prettyFormat.plugins @@ -46,7 +47,7 @@ function queryByLabelText(container, text, {selector = '*'} = {}) { function queryByText(container, text, {selector = '*'} = {}) { return ( Array.from(container.querySelectorAll(selector)).find(node => - matches(getText(node), node, text), + matches(getNodeText(node), node, text), ) || null ) } @@ -64,19 +65,6 @@ function queryByAttribute(attribute, container, text) { const queryByPlaceholderText = queryByAttribute.bind(null, 'placeholder') const queryByTestId = queryByAttribute.bind(null, 'data-testid') -// this is just a utility and not an exposed query. -// There are no plans to expose this. -function getText(node) { - return Array.from(node.childNodes) - .filter( - child => child.nodeType === Node.TEXT_NODE && Boolean(child.textContent), - ) - .map(c => c.textContent) - .join(' ') - .trim() - .replace(/\s+/g, ' ') -} - // getters // the reason we're not dynamically generating these functions that look so similar: // 1. The error messages are specific to each one and depend on arguments