diff --git a/src/utility/selectOptions.ts b/src/utility/selectOptions.ts index 29d4f3ca..dd93780d 100644 --- a/src/utility/selectOptions.ts +++ b/src/utility/selectOptions.ts @@ -2,6 +2,7 @@ import {getConfig} from '@testing-library/dom' import {hasPointerEvents, isDisabled, isElementType, wait} from '../utils' import {type Instance} from '../setup' import {focusElement} from '../event' +import {getVisibleText} from '../utils/misc/getVisibleText' export async function selectOptions( this: Instance, @@ -43,7 +44,7 @@ async function selectOptionsBase( const matchingOption = allOptions.find( o => (o as HTMLInputElement | HTMLTextAreaElement).value === val || - o.innerHTML === val, + getVisibleText(o as HTMLElement) === val, ) if (matchingOption) { return matchingOption diff --git a/src/utils/index.ts b/src/utils/index.ts index 8ab15cc8..b4d751e6 100644 --- a/src/utils/index.ts +++ b/src/utils/index.ts @@ -25,6 +25,7 @@ export * from './misc/findClosest' export * from './misc/getDocumentFromNode' export * from './misc/getTreeDiff' export * from './misc/getWindow' +export * from './misc/getVisibleText' export * from './misc/isDescendantOrSelf' export * from './misc/isElementType' export * from './misc/isVisible' diff --git a/src/utils/misc/getVisibleText.ts b/src/utils/misc/getVisibleText.ts new file mode 100644 index 00000000..89cdfde5 --- /dev/null +++ b/src/utils/misc/getVisibleText.ts @@ -0,0 +1,25 @@ +import {getWindow} from './getWindow' + +const removeNotVisibleChildren = (element: Element) => { + const window = getWindow(element) + for (const child of Array.from(element.children)) { + const {display, visibility} = window.getComputedStyle(child) + if ( + child.getAttribute('aria-hidden') || + display === 'none' || + visibility === 'hidden' + ) { + child.remove() + } else { + removeNotVisibleChildren(child) + } + } +} + +export const getVisibleText = (element: Element | null) => { + if (!element) return + + const clone = element.cloneNode(true) as Element + removeNotVisibleChildren(clone) + return clone.textContent?.trim().replace(/\s+/gm, ' ') +} diff --git a/tests/_helpers/listeners.ts b/tests/_helpers/listeners.ts index e66e7022..d4cd2b9e 100644 --- a/tests/_helpers/listeners.ts +++ b/tests/_helpers/listeners.ts @@ -1,5 +1,5 @@ import {eventMap} from '#src/event/eventMap' -import {isElementType} from '#src/utils' +import {isElementType, getVisibleText} from '#src/utils' import {MouseButton, MouseButtonFlip} from '#src/system/pointer/buttons' let eventListeners: Array<{ @@ -215,10 +215,12 @@ function getElementValue(element: Element) { return JSON.stringify(Array.from(element.selectedOptions).map(o => o.value)) } else if (element.getAttribute('role') === 'listbox') { return JSON.stringify( - element.querySelector('[aria-selected="true"]')?.innerHTML, + getVisibleText( + element.querySelector('[aria-selected="true"]') as Element, + ), ) } else if (element.getAttribute('role') === 'option') { - return JSON.stringify(element.innerHTML) + return JSON.stringify(getVisibleText(element)) } else if ('value' in element) { return JSON.stringify((element as HTMLInputElement).value) } diff --git a/tests/utility/selectOptions/_setup.ts b/tests/utility/selectOptions/_setup.ts index 5c72a9d2..432fbc1a 100644 --- a/tests/utility/selectOptions/_setup.ts +++ b/tests/utility/selectOptions/_setup.ts @@ -78,3 +78,56 @@ export function setupListbox() { user: userEvent.setup(), } } + +export function setupListboxWithComplexOptions() { + const wrapper = document.createElement('div') + wrapper.innerHTML = ` + +