Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: Add new custom matcher toHaveDescription (#244)
* Add toHaveDescription() matcher * Add toHaveDescription() docs
- Loading branch information
1 parent
2afc2c5
commit 943a0c9
Showing
5 changed files
with
248 additions
and
2 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,138 @@ | ||
import {render} from './helpers/test-utils' | ||
|
||
describe('.toHaveDescription', () => { | ||
test('handles positive test cases', () => { | ||
const {queryByTestId} = render(` | ||
<div id="description">The description</div> | ||
<div data-testid="single" aria-describedby="description"></div> | ||
<div data-testid="invalid_id" aria-describedby="invalid"></div> | ||
<div data-testid="without"></div> | ||
`) | ||
|
||
expect(queryByTestId('single')).toHaveDescription('The description') | ||
expect(queryByTestId('single')).toHaveDescription( | ||
expect.stringContaining('The'), | ||
) | ||
expect(queryByTestId('single')).toHaveDescription(/The/) | ||
expect(queryByTestId('single')).toHaveDescription( | ||
expect.stringMatching(/The/), | ||
) | ||
expect(queryByTestId('single')).toHaveDescription(/description/) | ||
expect(queryByTestId('single')).not.toHaveDescription('Something else') | ||
expect(queryByTestId('single')).not.toHaveDescription('The') | ||
|
||
expect(queryByTestId('invalid_id')).not.toHaveDescription() | ||
expect(queryByTestId('invalid_id')).toHaveDescription('') | ||
|
||
expect(queryByTestId('without')).not.toHaveDescription() | ||
expect(queryByTestId('without')).toHaveDescription('') | ||
}) | ||
|
||
test('handles multiple ids', () => { | ||
const {queryByTestId} = render(` | ||
<div id="first">First description</div> | ||
<div id="second">Second description</div> | ||
<div id="third">Third description</div> | ||
<div data-testid="multiple" aria-describedby="first second third"></div> | ||
`) | ||
|
||
expect(queryByTestId('multiple')).toHaveDescription( | ||
'First description Second description Third description', | ||
) | ||
expect(queryByTestId('multiple')).toHaveDescription( | ||
/Second description Third/, | ||
) | ||
expect(queryByTestId('multiple')).toHaveDescription( | ||
expect.stringContaining('Second description Third'), | ||
) | ||
expect(queryByTestId('multiple')).toHaveDescription( | ||
expect.stringMatching(/Second description Third/), | ||
) | ||
expect(queryByTestId('multiple')).not.toHaveDescription('Something else') | ||
expect(queryByTestId('multiple')).not.toHaveDescription('First') | ||
}) | ||
|
||
test('handles negative test cases', () => { | ||
const {queryByTestId} = render(` | ||
<div id="description">The description</div> | ||
<div data-testid="target" aria-describedby="description"></div> | ||
`) | ||
|
||
expect(() => | ||
expect(queryByTestId('other')).toHaveDescription('The description'), | ||
).toThrowError() | ||
|
||
expect(() => | ||
expect(queryByTestId('target')).toHaveDescription('Something else'), | ||
).toThrowError() | ||
|
||
expect(() => | ||
expect(queryByTestId('target')).not.toHaveDescription('The description'), | ||
).toThrowError() | ||
}) | ||
|
||
test('normalizes whitespace', () => { | ||
const {queryByTestId} = render(` | ||
<div id="first"> | ||
Step | ||
1 | ||
of | ||
4 | ||
</div> | ||
<div id="second"> | ||
And | ||
extra | ||
description | ||
</div> | ||
<div data-testid="target" aria-describedby="first second"></div> | ||
`) | ||
|
||
expect(queryByTestId('target')).toHaveDescription( | ||
'Step 1 of 4 And extra description', | ||
) | ||
}) | ||
|
||
test('can handle multiple levels with content spread across decendants', () => { | ||
const {queryByTestId} = render(` | ||
<span id="description"> | ||
<span>Step</span> | ||
<span> 1</span> | ||
<span><span>of</span></span> | ||
4</span> | ||
</span> | ||
<div data-testid="target" aria-describedby="description"></div> | ||
`) | ||
|
||
expect(queryByTestId('target')).toHaveDescription('Step 1 of 4') | ||
}) | ||
|
||
test('handles extra whitespace with multiple ids', () => { | ||
const {queryByTestId} = render(` | ||
<div id="first">First description</div> | ||
<div id="second">Second description</div> | ||
<div id="third">Third description</div> | ||
<div data-testid="multiple" aria-describedby=" first | ||
second third | ||
"></div> | ||
`) | ||
|
||
expect(queryByTestId('multiple')).toHaveDescription( | ||
'First description Second description Third description', | ||
) | ||
}) | ||
|
||
test('is case-sensitive', () => { | ||
const {queryByTestId} = render(` | ||
<span id="description">Sensitive text</span> | ||
<div data-testid="target" aria-describedby="description"></div> | ||
`) | ||
|
||
expect(queryByTestId('target')).toHaveDescription('Sensitive text') | ||
expect(queryByTestId('target')).not.toHaveDescription('sensitive text') | ||
}) | ||
}) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,42 @@ | ||
import {matcherHint, printExpected, printReceived} from 'jest-matcher-utils' | ||
import {checkHtmlElement, getMessage, normalize} from './utils' | ||
|
||
// See algoritm: https://www.w3.org/TR/accname-1.1/#mapping_additional_nd_description | ||
export function toHaveDescription(htmlElement, checkWith) { | ||
checkHtmlElement(htmlElement, toHaveDescription, this) | ||
|
||
const expectsDescription = checkWith !== undefined | ||
|
||
const descriptionIDRaw = htmlElement.getAttribute('aria-describedby') || '' | ||
const descriptionIDs = descriptionIDRaw.split(/\s+/).filter(Boolean) | ||
let description = '' | ||
if (descriptionIDs.length > 0) { | ||
const document = htmlElement.ownerDocument | ||
const descriptionEls = descriptionIDs | ||
.map(descriptionID => document.getElementById(descriptionID)) | ||
.filter(Boolean) | ||
description = normalize(descriptionEls.map(el => el.textContent).join(' ')) | ||
} | ||
|
||
return { | ||
pass: expectsDescription | ||
? checkWith instanceof RegExp | ||
? checkWith.test(description) | ||
: this.equals(description, checkWith) | ||
: Boolean(description), | ||
message: () => { | ||
const to = this.isNot ? 'not to' : 'to' | ||
return getMessage( | ||
matcherHint( | ||
`${this.isNot ? '.not' : ''}.toHaveDescription`, | ||
'element', | ||
'', | ||
), | ||
`Expected the element ${to} have description`, | ||
printExpected(checkWith), | ||
'Received', | ||
printReceived(description), | ||
) | ||
}, | ||
} | ||
} |