diff --git a/lib/html.js b/lib/html.js index 10027413f..3ed942b04 100644 --- a/lib/html.js +++ b/lib/html.js @@ -78,11 +78,12 @@ const defaultHtmlOpts = { textElements: ['label', 'h1', 'h2'], allowedAttrs: ['id', 'for', 'class', 'name', 'type', 'value', 'tabindex', 'aria-labelledby', 'aria-label', 'label', 'placeholder', 'title', 'alt', 'src', 'role'], allowedRoles: ['button', 'checkbox', 'search', 'textbox', 'tab'], + keepText: false, } function removeNonInteractiveElements(html, opts = {}) { opts = { ...defaultHtmlOpts, ...opts } - const { interactiveElements, textElements, allowedAttrs, allowedRoles } = opts + const { interactiveElements, textElements, allowedAttrs, allowedRoles, keepText } = opts // Parse the HTML into a document tree const document = parse(html) @@ -111,8 +112,14 @@ function removeNonInteractiveElements(html, opts = {}) { return false } + function hasVisibleText(node) { + if (node.nodeName === '#text') return !!node.value.trim() + return (node.childNodes || []).some(hasVisibleText) + } + function hasMeaningfulText(node) { if (textElements.includes(node.nodeName)) return true + if (keepText && hasVisibleText(node)) return true return false } @@ -294,7 +301,7 @@ function splitByChunks(text, chunkSize) { function simplifyHtmlElement(html, maxLength = 300) { try { - html = removeNonInteractiveElements(html) + html = removeNonInteractiveElements(html, { keepText: true }) html = html.replace(/(?:.*?<\/head>)?(.*)<\/body><\/html>/s, '$1').trim() } catch (e) { // keep raw html if minification fails diff --git a/test/unit/html_test.js b/test/unit/html_test.js index 1de0d444d..17e5dd577 100644 --- a/test/unit/html_test.js +++ b/test/unit/html_test.js @@ -3,7 +3,7 @@ import path from 'path' import { expect } from 'chai' import { fileURLToPath } from 'url' import * as cheerio from 'cheerio' -import { scanForErrorMessages, removeNonInteractiveElements, minifyHtml, splitByChunks, cleanHtml, formatHtml, isTrashClass } from '../../lib/html.js' +import { scanForErrorMessages, removeNonInteractiveElements, minifyHtml, splitByChunks, cleanHtml, formatHtml, isTrashClass, simplifyHtmlElement } from '../../lib/html.js' const __filename = fileURLToPath(import.meta.url) const __dirname = path.dirname(__filename) @@ -224,4 +224,35 @@ describe('HTML module', () => { expect(out).to.include('hi') }) }) + + describe('#simplifyHtmlElement', () => { + const button = label => + '` + + it('keeps the visible label when it is nested in non-interactive elements', () => { + const out = simplifyHtmlElement(button('New test')) + expect(out).to.include('New test') + expect(out).to.include('