diff --git a/src/__tests__/element-queries.js b/src/__tests__/element-queries.js
index 6a6df03b..61a88acf 100644
--- a/src/__tests__/element-queries.js
+++ b/src/__tests__/element-queries.js
@@ -352,9 +352,9 @@ test('label with no form control', () => {
`)
})
-test('label with no form control and fuzzy matcher', () => {
+test('label with "for" attribute but no form control and fuzzy matcher', () => {
const {getByLabelText, queryByLabelText} = render(
- ``,
+ ``,
)
expect(queryByLabelText('alone', {exact: false})).toBeNull()
expect(() => getByLabelText('alone', {exact: false}))
@@ -362,7 +362,9 @@ test('label with no form control and fuzzy matcher', () => {
"Found a label with the text of: alone, however no form control was found associated to that label. Make sure you're using the "for" attribute or "aria-labelledby" attribute correctly.
-
"
@@ -407,6 +409,114 @@ test('label with children with no form control', () => {
`)
})
+test('label with non-labellable element', () => {
+ const {getByLabelText, queryByLabelText} = render(`
+
+ Label 1
+
+ Hello
+
+
+ `)
+
+ expect(queryByLabelText(/Label/)).toBeNull()
+ expect(() => getByLabelText(/Label/)).toThrowErrorMatchingInlineSnapshot(`
+"Found a label with the text of: /Label/, however the element associated with this label () is non-labellable [https://html.spec.whatwg.org/multipage/forms.html#category-label]. If you really need to label a , you can use aria-label or aria-labelledby instead.
+
+
+ `)
+
+ expect(queryAllByLabelText(/Label/)).toEqual([])
+ expect(() => getAllByLabelText(/Label/)).toThrowErrorMatchingInlineSnapshot(`
+"Found a label with the text of: /Label/, however the element associated with this label () is non-labellable [https://html.spec.whatwg.org/multipage/forms.html#category-label]. If you really need to label a , you can use aria-label or aria-labelledby instead.
+
+Found a label with the text of: /Label/, however the element associated with this label () is non-labellable [https://html.spec.whatwg.org/multipage/forms.html#category-label]. If you really need to label a , you can use aria-label or aria-labelledby instead.
+
+
"
+`)
+})
+
test('totally empty label', () => {
const {getByLabelText, queryByLabelText} = render(``)
expect(queryByLabelText('')).toBeNull()
diff --git a/src/queries/label-text.js b/src/queries/label-text.js
index 9f28f9dd..9f959832 100644
--- a/src/queries/label-text.js
+++ b/src/queries/label-text.js
@@ -149,10 +149,27 @@ const getAllByLabelText = (container, text, ...rest) => {
if (!els.length) {
const labels = queryAllLabelsByText(container, text, ...rest)
if (labels.length) {
- throw getConfig().getElementError(
- `Found a label with the text of: ${text}, however no form control was found associated to that label. Make sure you're using the "for" attribute or "aria-labelledby" attribute correctly.`,
- container,
- )
+ const tagNames = labels
+ .map(label =>
+ getTagNameOfElementAssociatedWithLabelViaFor(container, label),
+ )
+ .filter(tagName => !!tagName)
+ if (tagNames.length) {
+ throw getConfig().getElementError(
+ tagNames
+ .map(
+ tagName =>
+ `Found a label with the text of: ${text}, however the element associated with this label (<${tagName} />) is non-labellable [https://html.spec.whatwg.org/multipage/forms.html#category-label]. If you really need to label a <${tagName} />, you can use aria-label or aria-labelledby instead.`,
+ )
+ .join('\n\n'),
+ container,
+ )
+ } else {
+ throw getConfig().getElementError(
+ `Found a label with the text of: ${text}, however no form control was found associated to that label. Make sure you're using the "for" attribute or "aria-labelledby" attribute correctly.`,
+ container,
+ )
+ }
} else {
throw getConfig().getElementError(
`Unable to find a label with the text of: ${text}`,
@@ -163,6 +180,16 @@ const getAllByLabelText = (container, text, ...rest) => {
return els
}
+function getTagNameOfElementAssociatedWithLabelViaFor(container, label) {
+ const htmlFor = label.getAttribute('for')
+ if (!htmlFor) {
+ return null
+ }
+
+ const element = container.querySelector(`[id="${htmlFor}"]`)
+ return element ? element.tagName.toLowerCase() : null
+}
+
// the reason mentioned above is the same reason we're not using buildQueries
const getMultipleError = (c, text) =>
`Found multiple elements with the text of: ${text}`