Skip to content

Commit bc65b91

Browse files
authored
fix: getByLabelText for output (#789)
1 parent 53829b8 commit bc65b91

File tree

4 files changed

+69
-33
lines changed

4 files changed

+69
-33
lines changed

src/__tests__/element-queries.js

Lines changed: 31 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -293,7 +293,7 @@ test('returns the labelable element control inside a label', () => {
293293
<output />
294294
<progress />
295295
<select />
296-
<textarea />
296+
<textarea ></textarea>
297297
</label>
298298
`)
299299

@@ -558,7 +558,7 @@ test('query/get element by its title', () => {
558558
expect(getByTitle('Delete').id).toEqual('2')
559559
expect(queryByTitle('Delete').id).toEqual('2')
560560
expect(queryByTitle('Del', {exact: false}).id).toEqual('2')
561-
expect(queryByTitle("HelloWorld")).toBeNull()
561+
expect(queryByTitle('HelloWorld')).toBeNull()
562562
})
563563

564564
test('query/get title element of SVG', () => {
@@ -1200,3 +1200,32 @@ it('gets form controls by label text on IE and other legacy browsers', () => {
12001200
`)
12011201
expect(getByLabelText('Label text').id).toBe('input-id')
12021202
})
1203+
1204+
// https://github.com/testing-library/dom-testing-library/issues/787
1205+
it(`get the output element by it's label`, () => {
1206+
const {getByLabelText, rerender} = renderIntoDocument(`
1207+
<label>foo
1208+
<output>bar</output>
1209+
</label>
1210+
`)
1211+
expect(getByLabelText('foo')).toBeInTheDocument()
1212+
1213+
rerender(`
1214+
<label>
1215+
<small>foo</small>
1216+
<output>bar</output>
1217+
</label>
1218+
`)
1219+
1220+
expect(getByLabelText('foo')).toBeInTheDocument()
1221+
})
1222+
1223+
// https://github.com/testing-library/dom-testing-library/issues/343#issuecomment-555385756
1224+
it(`should get element by it's label when there are elements with same text`, () => {
1225+
const {getByLabelText} = renderIntoDocument(`
1226+
<label>test 1
1227+
<textarea>test</textarea>
1228+
</label>
1229+
`)
1230+
expect(getByLabelText('test 1')).toBeInTheDocument()
1231+
})

src/get-node-text.js

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,4 @@
1-
// Constant node.nodeType for text nodes, see:
2-
// https://developer.mozilla.org/en-US/docs/Web/API/Node/nodeType#Node_type_constants
3-
const TEXT_NODE = 3
1+
const {TEXT_NODE} = require('./helpers')
42

53
function getNodeText(node) {
64
if (node.matches('input[type=submit], input[type=button]')) {

src/helpers.js

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,7 @@
11
const globalObj = typeof window === 'undefined' ? global : window
2+
// Constant node.nodeType for text nodes, see:
3+
// https://developer.mozilla.org/en-US/docs/Web/API/Node/nodeType#Node_type_constants
4+
const TEXT_NODE = 3
25

36
// Currently this fn only supports jest timers, but it could support other test runners in the future.
47
function runWithRealTimers(callback) {
@@ -127,4 +130,5 @@ export {
127130
runWithRealTimers,
128131
checkContainerType,
129132
jestFakeTimersAreEnabled,
133+
TEXT_NODE,
130134
}

src/queries/label-text.js

Lines changed: 33 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import {getConfig} from '../config'
2-
import {checkContainerType} from '../helpers'
2+
import {checkContainerType, TEXT_NODE} from '../helpers'
33
import {
44
fuzzyMatches,
55
matches,
@@ -11,25 +11,20 @@ import {
1111
wrapSingleQueryWithSuggestion,
1212
} from './all-utils'
1313

14+
const labelledNodeNames = [
15+
'button',
16+
'meter',
17+
'output',
18+
'progress',
19+
'select',
20+
'textarea',
21+
'input',
22+
]
23+
1424
function queryAllLabels(container) {
1525
return Array.from(container.querySelectorAll('label,input'))
1626
.map(node => {
17-
let textToMatch =
18-
node.tagName.toLowerCase() === 'label'
19-
? node.textContent
20-
: node.value || null
21-
// The children of a textarea are part of `textContent` as well. We
22-
// need to remove them from the string so we can match it afterwards.
23-
Array.from(node.querySelectorAll('textarea')).forEach(textarea => {
24-
textToMatch = textToMatch.replace(textarea.value, '')
25-
})
26-
27-
// The children of a select are also part of `textContent`, so we
28-
// need also to remove their text.
29-
Array.from(node.querySelectorAll('select')).forEach(select => {
30-
textToMatch = textToMatch.replace(select.textContent, '')
31-
})
32-
return {node, textToMatch}
27+
return {node, textToMatch: getLabelContent(node)}
3328
})
3429
.filter(({textToMatch}) => textToMatch !== null)
3530
}
@@ -51,15 +46,26 @@ function queryAllLabelsByText(
5146
.map(({node}) => node)
5247
}
5348

54-
function getLabelContent(label) {
55-
let labelContent = label.getAttribute('value') || label.textContent
56-
Array.from(label.querySelectorAll('textarea')).forEach(textarea => {
57-
labelContent = labelContent.replace(textarea.value, '')
58-
})
59-
Array.from(label.querySelectorAll('select')).forEach(select => {
60-
labelContent = labelContent.replace(select.textContent, '')
61-
})
62-
return labelContent
49+
function getTextContent(node) {
50+
if (labelledNodeNames.includes(node.nodeName.toLowerCase())) {
51+
return ''
52+
}
53+
54+
if (node.nodeType === TEXT_NODE) return node.textContent
55+
56+
return Array.from(node.childNodes)
57+
.map(childNode => getTextContent(childNode))
58+
.join('')
59+
}
60+
61+
function getLabelContent(node) {
62+
let textContent
63+
if (node.tagName.toLowerCase() === 'label') {
64+
textContent = getTextContent(node)
65+
} else {
66+
textContent = node.value || node.textContent
67+
}
68+
return textContent
6369
}
6470

6571
function queryAllByLabelText(
@@ -88,8 +94,7 @@ function queryAllByLabelText(
8894
})
8995
: Array.from(getLabels(labelledElement)).map(label => {
9096
const textToMatch = getLabelContent(label)
91-
const formControlSelector =
92-
'button, input, meter, output, progress, select, textarea'
97+
const formControlSelector = labelledNodeNames.join(',')
9398
const labelledFormControl = Array.from(
9499
label.querySelectorAll(formControlSelector),
95100
).filter(element => element.matches(selector))[0]

0 commit comments

Comments
 (0)