dom-testing-library icon indicating copy to clipboard operation
dom-testing-library copied to clipboard

`getByTitle` for SVGs canot find `title` if it's not a direct child of `svg`

Open rodoabad opened this issue 3 years ago • 8 comments

  • @testing-library/react version: 11.2.7
  • Testing Framework and version: jest 26x || 27x
  • DOM Environment: 'jsdom' 16.x

Relevant code or config:

Source: https://testing-library.com/docs/queries/bytitle/

    const Test = () => (
        <>
            <span title="Delete" id="2"></span>
            <svg>
                <title>Close</title>
                <g><path /></g>
            </svg>
        </>
    );

VS

    const Test = () => (
        <>
            <span title="Delete" id="2"></span>
            <svg>
                <g>
                    <path>
                         <title>Close</title>
                    </path>
                </g>
            </svg>
       </>
    );

What you did:

render(<Test/>);

screen.getByTitle('Close');

What happened:

First Component - ✅ Second Component - ❌

TestingLibraryElementError: Unable to find an element with the title: Close.

Problem description:

Testing library cannot find Close because it's not a direct child of svg.

Suggested solution:

I should be able to find Close even if it's not a direct child of svg.

rodoabad avatar Jun 09 '21 18:06 rodoabad

Thanks for the feedback

To be clear: The query should return the <path /> not the <svg /> as far as I understood SVG title element.

It sounds like we should implement walking up the tree until we find an SVG container or graphics element.

eps1lon avatar Jun 09 '21 19:06 eps1lon

@eps1lon just wondering as a stop-gap, how would you query title in this case as it stands right now?

rodoabad avatar Jun 09 '21 19:06 rodoabad

Just guessing here: getByText("Close", selector: 'title') and then walk up to the nearest SVG container or graphics element.

eps1lon avatar Jun 09 '21 20:06 eps1lon

@eps1lon any updates for this issue?

rodoabad avatar Dec 07 '21 21:12 rodoabad

@eps1lon any updates for this issue?

PRs welcome for https://github.com/testing-library/dom-testing-library/issues/974#issuecomment-858028323

eps1lon avatar Dec 07 '21 21:12 eps1lon

@eps1lon, just wondering if there's really a need to climb up the tree or just the immediate parent is enough , so instead of the implementation we have, we can do something like this:

const SVGContainerAndGraphicsElements = [
  'svg',
  'a',
  'defs',
  'g',
  'marker',
  'mask',
  'missingglyph',
  'pattern',
  'svg',
  'switch',
  'symbol',
  'circle',
  'ellipse',
  'image',
  'line',
  'path',
  'polygon',
  'polyline',
  'rect',
  'text',
  'use',
]

const isSvgTitle = (node: HTMLElement) =>
  node.tagName.toLowerCase() === 'title' &&
  node.parentElement &&
  SVGContainerAndGraphicsElements.includes(
    node.parentElement.tagName.toLowerCase(),
  )

Wdyt?

MatanBobi avatar Jan 15 '22 09:01 MatanBobi

That implementation seems too clever. It breaks as soon as any of these names and nesting are valid HTML.

Let's make it work first and then check if we need a different implementation.

eps1lon avatar Jan 15 '22 16:01 eps1lon

I guess the code would need to traverse up O(n), so would something like this make sense?

const isWithinSvg = (node: HTMLElement) => {
  const curParent = node.parentElement
  while (curParent != null) {
    if (curParent.tagName.toLowerCase() === 'svg') return true
    curParent = curParent.parentElement
  }
  return false
}

const isSvgTitle = (node: HTMLElement) =>
  node.tagName.toLowerCase() === 'title' && isWithinSvg(node)

marioestrada avatar Jun 03 '22 17:06 marioestrada