Created
February 1, 2021 17:57
-
-
Save carlobeltrame/b6b2f9a55455b6b38fa01f4a4a11d750 to your computer and use it in GitHub Desktop.
A custom Jest matcher in the style of jest-dom, with the power to test only for visible text
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import {checkHtmlElement, getMessage, matches, normalize} from '@testing-library/jest-dom/dist/utils' | |
function isStyleVisible(element) { | |
const {getComputedStyle} = element.ownerDocument.defaultView | |
const {display, visibility, opacity} = getComputedStyle(element) | |
return ( | |
display !== 'none' && | |
visibility !== 'hidden' && | |
visibility !== 'collapse' && | |
opacity !== '0' && | |
opacity !== 0 | |
) | |
} | |
function isAttributeVisible(element, previousElement) { | |
return ( | |
!element.hasAttribute('hidden') && | |
(element.nodeName === 'DETAILS' && previousElement.nodeName !== 'SUMMARY' | |
? element.hasAttribute('open') | |
: true) | |
) | |
} | |
function isElementVisible(element, previousElement) { | |
return ( | |
isStyleVisible(element) && | |
isAttributeVisible(element, previousElement) && | |
(!element.parentElement || isElementVisible(element.parentElement, element)) | |
) | |
} | |
function getVisibleTextContent(htmlElement) { | |
let result = '' | |
const nodeType = htmlElement.nodeType | |
if (nodeType === 3) return htmlElement.nodeValue | |
if (nodeType !== 1 && nodeType !== 9 && nodeType !== 11) return '' | |
if (!isElementVisible(htmlElement)) return '' | |
if (nodeType === 1 || nodeType === 9 || nodeType === 11) { | |
htmlElement.childNodes.forEach(child => { | |
result += ' ' + getVisibleTextContent(child) | |
}) | |
} | |
return result.trim(); | |
} | |
function toHaveVisibleTextContent( | |
htmlElement, | |
checkWith = true, | |
options = {normalizeWhitespace: true}, | |
) { | |
checkHtmlElement(htmlElement, toHaveVisibleTextContent, this) | |
const textContent = options.normalizeWhitespace | |
? normalize(getVisibleTextContent(htmlElement)) | |
: getVisibleTextContent(htmlElement).replace(/\u00a0/g, ' ') // Replace with normal spaces | |
if (checkWith === true) { | |
return { | |
pass: Boolean(textContent), | |
message: () => { | |
const to = this.isNot ? 'not to' : 'to' | |
return getMessage( | |
this, | |
this.utils.matcherHint( | |
`${this.isNot ? '.not' : ''}.toHaveVisibleTextContent`, | |
'element', | |
'', | |
), | |
`Expected element ${to} have any visible text content`, | |
undefined, | |
'Received', | |
textContent, | |
) | |
}, | |
} | |
} | |
const checkingWithEmptyString = textContent !== '' && checkWith === '' | |
return { | |
pass: !checkingWithEmptyString && matches(textContent, checkWith), | |
message: () => { | |
const to = this.isNot ? 'not to' : 'to' | |
return getMessage( | |
this, | |
this.utils.matcherHint( | |
`${this.isNot ? '.not' : ''}.toHaveVisibleTextContent`, | |
'element', | |
'', | |
), | |
checkingWithEmptyString | |
? `Checking with empty string will always match, use the matcher with no argument instead` | |
: `Expected element ${to} have visible text content`, | |
checkWith, | |
'Received', | |
textContent, | |
) | |
}, | |
} | |
} | |
expect.extend({ toHaveVisibleTextContent }) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment