diff --git a/src/__tests__/suggestions.js b/src/__tests__/suggestions.js index 485299d5..e0ecd51c 100644 --- a/src/__tests__/suggestions.js +++ b/src/__tests__/suggestions.js @@ -35,6 +35,18 @@ test('respects ignores', () => { ).not.toThrowError() }) +test('does not suggest query that would give a different element', () => { + renderIntoDocument(` +
+
+

link text

+ `) + + expect(() => screen.getByTestId('foo')).not.toThrowError() + expect(() => screen.getByTestId('bar')).not.toThrowError() + expect(() => screen.getByTestId('baz')).not.toThrowError() +}) + test('does not suggest when using getByRole', () => { renderIntoDocument(``) @@ -57,6 +69,12 @@ test(`should not suggest if the suggestion would give different results`, () => ).not.toThrowError() }) +test('should suggest by label over title', () => { + renderIntoDocument(``) + + expect(() => screen.getByTitle('foo')).toThrowError(/getByLabelText\("bar"\)/) +}) + test('should not suggest if there would be mixed suggestions', () => { renderIntoDocument(` @@ -145,6 +163,16 @@ test('should suggest img role w/ alt text', () => { ) }) +test('escapes regular expressions in suggestion', () => { + renderIntoDocument( + `The Problem (picture of a question mark)`, + ) + + expect(() => screen.getByTestId('foo')).toThrowError( + /getByRole\("img", \{name: \/the problem \\\(picture of a question mark\\\)\/i\}\)/, + ) +}) + test('should suggest getByLabelText when no role available', () => { renderIntoDocument( ``, diff --git a/src/suggestions.js b/src/suggestions.js index 20735889..d67b373b 100644 --- a/src/suggestions.js +++ b/src/suggestions.js @@ -1,8 +1,8 @@ import {computeAccessibleName} from 'dom-accessibility-api' -import {getRoles} from './role-helpers' import {getDefaultNormalizer} from './matches' import {getNodeText} from './get-node-text' import {DEFAULT_IGNORE_TAGS} from './config' +import {getImplicitAriaRoles} from './role-helpers' const normalize = getDefaultNormalizer() @@ -26,23 +26,25 @@ function getLabelTextFor(element) { } return undefined } - +function escapeRegExp(string) { + return string.replace(/[.*+\-?^${}()|[\]\\]/g, '\\$&') // $& means the whole matched string +} function makeSuggestion(queryName, content, {variant, name}) { return { queryName, toString() { - const options = name ? `, {name: /${name.toLowerCase()}/i}` : '' + const options = name + ? `, {name: /${escapeRegExp(name.toLowerCase())}/i}` + : '' return `${variant}By${queryName}("${content}"${options})` }, } } export function getSuggestedQuery(element, variant) { - const roles = getRoles(element) - - const roleNames = Object.keys(roles) - if (roleNames.length) { - const [role] = roleNames + const role = + element.getAttribute('role') ?? getImplicitAriaRoles(element)?.[0] + if (role) { return makeSuggestion('Role', role, { variant, name: computeAccessibleName(element),