Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow to use regex on toHaveDisplayValue #242

Merged
merged 10 commits into from May 7, 2020
13 changes: 10 additions & 3 deletions README.md
Expand Up @@ -46,6 +46,7 @@ clear to read and to maintain.
<!-- START doctoc generated TOC please keep comment here to allow auto update -->
<!-- DON'T EDIT THIS SECTION, INSTEAD RE-RUN doctoc TO UPDATE -->


- [Installation](#installation)
- [Usage](#usage)
- [Custom matchers](#custom-matchers)
Expand Down Expand Up @@ -86,9 +87,11 @@ should be installed as one of your project's `devDependencies`:
```
npm install --save-dev @testing-library/jest-dom
```
or

or

for installation with [yarn](https://yarnpkg.com/) package manager.

```
yarn add --dev @testing-library/jest-dom
```
Expand Down Expand Up @@ -778,7 +781,7 @@ expect(selectInput).not.toHaveValue(['second', 'third'])
### `toHaveDisplayValue`

```typescript
toHaveDisplayValue(value: string | string[])
toHaveDisplayValue(value: string | RegExp | (string|RegExp)[])
```

This allows you to check whether the given form element has the specified
Expand Down Expand Up @@ -823,9 +826,12 @@ const selectSingle = screen.getByLabelText('Fruit')
const selectMultiple = screen.getByLabelText('Fruits')

expect(input).toHaveDisplayValue('Luca')
expect(input).toHaveDisplayValue(/Luc/)
expect(textarea).toHaveDisplayValue('An example description here.')
expect(textarea).toHaveDisplayValue(/example/)
expect(selectSingle).toHaveDisplayValue('Select a fruit...')
expect(selectMultiple).toHaveDisplayValue(['Banana', 'Avocado'])
expect(selectSingle).toHaveDisplayValue(/Select/)
expect(selectMultiple).toHaveDisplayValue([/Avocado/, 'Banana'])
```

<hr />
Expand Down Expand Up @@ -1026,6 +1032,7 @@ Thanks goes to these people ([emoji key][emojis]):

<!-- markdownlint-enable -->
<!-- prettier-ignore-end -->

<!-- ALL-CONTRIBUTORS-LIST:END -->

This project follows the [all-contributors][all-contributors] specification.
Expand Down
81 changes: 59 additions & 22 deletions src/__tests__/to-have-display-value.js
Expand Up @@ -21,36 +21,71 @@ test('it should work as expected', () => {

queryByTestId('select').value = 'banana'
expect(queryByTestId('select')).toHaveDisplayValue('Banana')
expect(queryByTestId('select')).toHaveDisplayValue(/[bB]ana/)
})

test('it should work with select multiple', () => {
const {queryByTestId} = render(`
<select id="fruits" data-testid="select" multiple>
<option value="">Select a fruit...</option>
<option value="ananas" selected>Ananas</option>
<option value="banana">Banana</option>
<option value="avocado" selected>Avocado</option>
</select>
`)
describe('with multiple select', () => {
let subject
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We'd rather not share a variable across tests. No need for this or the afterEach that resets it.

If you need to reuse the rendering part, extract it to a function in the module scope and call that function on each test that needs it.


expect(queryByTestId('select')).toHaveDisplayValue(['Ananas', 'Avocado'])
expect(() =>
expect(queryByTestId('select')).not.toHaveDisplayValue([
afterEach(() => {
subject = undefined
})

beforeEach(() => {
subject = render(`
<select id="fruits" data-testid="select" multiple>
<option value="">Select a fruit...</option>
<option value="ananas" selected>Ananas</option>
<option value="banana">Banana</option>
<option value="avocado" selected>Avocado</option>
</select>
`)
})

it('matches only when all the multiple selected values are equal to all the expected values', () => {
expect(subject.queryByTestId('select')).toHaveDisplayValue([
'Ananas',
'Avocado',
]),
).toThrow()

expect(queryByTestId('select')).not.toHaveDisplayValue('Ananas')
expect(() =>
expect(queryByTestId('select')).toHaveDisplayValue('Ananas'),
).toThrow()
])
expect(() =>
expect(subject.queryByTestId('select')).not.toHaveDisplayValue([
'Ananas',
'Avocado',
]),
).toThrow()
expect(subject.queryByTestId('select')).not.toHaveDisplayValue([
'Ananas',
'Avocado',
'Orange',
])
expect(subject.queryByTestId('select')).not.toHaveDisplayValue('Ananas')
expect(() =>
expect(subject.queryByTestId('select')).toHaveDisplayValue('Ananas'),
).toThrow()

Array.from(subject.queryByTestId('select').options).forEach(option => {
option.selected = ['ananas', 'banana'].includes(option.value)
})

expect(subject.queryByTestId('select')).toHaveDisplayValue([
'Ananas',
'Banana',
])
})

Array.from(queryByTestId('select').options).forEach(option => {
option.selected = ['ananas', 'banana'].includes(option.value)
it('matches even when the expected values are unordered', () => {
expect(subject.queryByTestId('select')).toHaveDisplayValue([
'Avocado',
'Ananas',
])
})

expect(queryByTestId('select')).toHaveDisplayValue(['Ananas', 'Banana'])
it('matches with regex expected values', () => {
expect(subject.queryByTestId('select')).toHaveDisplayValue([
/[Aa]nanas/,
'Avocado',
])
})
})

test('it should work with input elements', () => {
Expand All @@ -59,6 +94,7 @@ test('it should work with input elements', () => {
`)

expect(queryByTestId('input')).toHaveDisplayValue('Luca')
expect(queryByTestId('input')).toHaveDisplayValue(/Luc/)

queryByTestId('input').value = 'Piero'
expect(queryByTestId('input')).toHaveDisplayValue('Piero')
Expand All @@ -72,6 +108,7 @@ test('it should work with textarea elements', () => {
expect(queryByTestId('textarea-example')).toHaveDisplayValue(
'An example description here.',
)
expect(queryByTestId('textarea-example')).toHaveDisplayValue(/example/)

queryByTestId('textarea-example').value = 'Another example'
expect(queryByTestId('textarea-example')).toHaveDisplayValue(
Expand Down
42 changes: 31 additions & 11 deletions src/to-have-display-value.js
@@ -1,6 +1,5 @@
import {matcherHint} from 'jest-matcher-utils'

import {checkHtmlElement, getMessage} from './utils'
import {matches, checkHtmlElement, getMessage} from './utils'

export function toHaveDisplayValue(htmlElement, expectedValue) {
checkHtmlElement(htmlElement, toHaveDisplayValue, this)
Expand All @@ -18,16 +17,19 @@ export function toHaveDisplayValue(htmlElement, expectedValue) {
)
}

const value =
tagName === 'select'
? Array.from(htmlElement)
.filter(option => option.selected)
.map(option => option.textContent)
.toString()
: htmlElement.value
const values = getValues(tagName, htmlElement)
const expectedValues = getExpectedValues(expectedValue)
const qtyOfMatchesWithValues = getQtyOfMatchesBetweenArrays(
values,
expectedValues,
)

const matchedWithAllValues = qtyOfMatchesWithValues === values.length
const matchedWithAllExpectedValues =
qtyOfMatchesWithValues === expectedValues.length

return {
pass: value === expectedValue.toString(),
pass: matchedWithAllValues && matchedWithAllExpectedValues,
message: () =>
getMessage(
matcherHint(
Expand All @@ -38,7 +40,25 @@ export function toHaveDisplayValue(htmlElement, expectedValue) {
`Expected element ${this.isNot ? 'not ' : ''}to have display value`,
expectedValue,
'Received',
value,
values,
),
}
}

function getValues(tagName, htmlElement) {
return tagName === 'select'
? Array.from(htmlElement)
.filter(option => option.selected)
.map(option => option.textContent)
: [htmlElement.value]
}

function getExpectedValues(expectedValue) {
return expectedValue instanceof Array ? expectedValue : [expectedValue]
}

function getQtyOfMatchesBetweenArrays(arrayBase, array) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd rather have a fully readable name like getNumberOfMatchesBetweenArrays. Same for the variables above that store this value.

return array.filter(
expected => arrayBase.filter(value => matches(value, expected)).length,
).length
}