Skip to content

Commit 30a1ee8

Browse files
authored
feat(suggestions): add option to get specific query suggestions (#627)
1 parent b9900f8 commit 30a1ee8

File tree

2 files changed

+119
-10
lines changed

2 files changed

+119
-10
lines changed

src/__tests__/suggestions.js

Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -353,3 +353,95 @@ test('getSuggestedQuery returns rich data for tooling', () => {
353353

354354
expect(getSuggestedQuery(div).toString()).toEqual(`getByText(/cancel/i)`)
355355
})
356+
357+
test('getSuggestedQuery can return specified methods in addition to the best', () => {
358+
const {container} = render(`
359+
<label for="username">label</label>
360+
<input
361+
id="username"
362+
name="name"
363+
placeholder="placeholder"
364+
data-testid="testid"
365+
title="title"
366+
alt="alt"
367+
value="value"
368+
type="text"
369+
/>
370+
<button>button</button>
371+
`)
372+
373+
const input = container.querySelector('input')
374+
const button = container.querySelector('button')
375+
376+
expect(getSuggestedQuery(input, 'get', 'Role')).toMatchObject({
377+
queryName: 'Role',
378+
queryMethod: 'getByRole',
379+
queryArgs: ['textbox', {name: /label/i}],
380+
variant: 'get',
381+
})
382+
383+
expect(getSuggestedQuery(input, 'get', 'LabelText')).toMatchObject({
384+
queryName: 'LabelText',
385+
queryMethod: 'getByLabelText',
386+
queryArgs: [/label/i],
387+
variant: 'get',
388+
})
389+
390+
expect(getSuggestedQuery(input, 'get', 'PlaceholderText')).toMatchObject({
391+
queryName: 'PlaceholderText',
392+
queryMethod: 'getByPlaceholderText',
393+
queryArgs: [/placeholder/i],
394+
variant: 'get',
395+
})
396+
397+
expect(getSuggestedQuery(button, 'get', 'Text')).toMatchObject({
398+
queryName: 'Text',
399+
queryMethod: 'getByText',
400+
queryArgs: [/button/],
401+
variant: 'get',
402+
})
403+
404+
expect(getSuggestedQuery(input, 'get', 'DisplayValue')).toMatchObject({
405+
queryName: 'DisplayValue',
406+
queryMethod: 'getByDisplayValue',
407+
queryArgs: [/value/i],
408+
variant: 'get',
409+
})
410+
411+
expect(getSuggestedQuery(input, 'get', 'AltText')).toMatchObject({
412+
queryName: 'AltText',
413+
queryMethod: 'getByAltText',
414+
queryArgs: [/alt/],
415+
variant: 'get',
416+
})
417+
418+
expect(getSuggestedQuery(input, 'get', 'Title')).toMatchObject({
419+
queryName: 'Title',
420+
queryMethod: 'getByTitle',
421+
queryArgs: [/title/i],
422+
variant: 'get',
423+
})
424+
425+
expect(getSuggestedQuery(input, 'get', 'TestId')).toMatchObject({
426+
queryName: 'TestId',
427+
queryMethod: 'getByTestId',
428+
queryArgs: ['testid'],
429+
variant: 'get',
430+
})
431+
432+
// return undefined if requested query can't be made
433+
expect(getSuggestedQuery(button, 'get', 'TestId')).toBeUndefined()
434+
})
435+
436+
test('getSuggestedQuery does not create suggestions for script and style elements', () => {
437+
const {container} = render(`
438+
<script data-testid="script"></script>
439+
<style data-testid="style"></style>
440+
`)
441+
442+
const script = container.querySelector('script')
443+
const style = container.querySelector('style')
444+
445+
expect(getSuggestedQuery(script, 'get', 'TestId')).toBeUndefined()
446+
expect(getSuggestedQuery(style, 'get', 'TestId')).toBeUndefined()
447+
})

src/suggestions.js

Lines changed: 27 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,11 @@ function getRegExpMatcher(string) {
3535
}
3636

3737
function makeSuggestion(queryName, content, {variant = 'get', name}) {
38-
const queryArgs = [queryName === 'Role' ? content : getRegExpMatcher(content)]
38+
const queryArgs = [
39+
queryName === 'Role' || queryName === 'TestId'
40+
? content
41+
: getRegExpMatcher(content),
42+
]
3943

4044
if (name) {
4145
queryArgs.push({name: new RegExp(escapeRegExp(name.toLowerCase()), 'i')})
@@ -64,45 +68,58 @@ function makeSuggestion(queryName, content, {variant = 'get', name}) {
6468
}
6569
}
6670

67-
export function getSuggestedQuery(element, variant) {
71+
function canSuggest(currentMethod, requestedMethod, data) {
72+
return data && (!requestedMethod || requestedMethod === currentMethod)
73+
}
74+
75+
export function getSuggestedQuery(element, variant, method) {
76+
// don't create suggestions for script and style elements
77+
if (element.matches(DEFAULT_IGNORE_TAGS)) {
78+
return undefined
79+
}
80+
6881
const role =
6982
element.getAttribute('role') ?? getImplicitAriaRoles(element)?.[0]
70-
if (role) {
83+
if (canSuggest('Role', method, role)) {
7184
return makeSuggestion('Role', role, {
7285
variant,
7386
name: computeAccessibleName(element),
7487
})
7588
}
7689

7790
const labelText = getLabelTextFor(element)
78-
if (labelText) {
91+
if (canSuggest('LabelText', method, labelText)) {
7992
return makeSuggestion('LabelText', labelText, {variant})
8093
}
8194

8295
const placeholderText = element.getAttribute('placeholder')
83-
if (placeholderText) {
96+
if (canSuggest('PlaceholderText', method, placeholderText)) {
8497
return makeSuggestion('PlaceholderText', placeholderText, {variant})
8598
}
8699

87100
const textContent = normalize(getNodeText(element))
88-
if (textContent && !element.matches(DEFAULT_IGNORE_TAGS)) {
101+
if (canSuggest('Text', method, textContent)) {
89102
return makeSuggestion('Text', textContent, {variant})
90103
}
91104

92-
if (element.value) {
105+
if (canSuggest('DisplayValue', method, element.value)) {
93106
return makeSuggestion('DisplayValue', normalize(element.value), {variant})
94107
}
95108

96109
const alt = element.getAttribute('alt')
97-
if (alt) {
110+
if (canSuggest('AltText', method, alt)) {
98111
return makeSuggestion('AltText', alt, {variant})
99112
}
100113

101114
const title = element.getAttribute('title')
102-
103-
if (title) {
115+
if (canSuggest('Title', method, title)) {
104116
return makeSuggestion('Title', title, {variant})
105117
}
106118

119+
const testId = element.getAttribute('data-testid')
120+
if (canSuggest('TestId', method, testId)) {
121+
return makeSuggestion('TestId', testId, {variant})
122+
}
123+
107124
return undefined
108125
}

0 commit comments

Comments
 (0)