Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
feat: Allow to use regex on toHaveDisplayValue (#242)
* Allow to use regex on non-multiple value on toHaveDisplayValue

* Allow to use regex on multiple value on toHaveDisplayValue

* Do some refactor

* Split specs in contexts

* Update docs to describe the unordered multiple match

* Add review

Co-authored-by: Ernesto García <gnapse@gmail.com>
  • Loading branch information
lourenci and gnapse committed May 7, 2020
1 parent 17787f3 commit 5c9e8e5
Show file tree
Hide file tree
Showing 3 changed files with 94 additions and 35 deletions.
9 changes: 7 additions & 2 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 @@ -781,7 +782,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 @@ -826,9 +827,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 @@ -1084,6 +1088,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
78 changes: 56 additions & 22 deletions src/__tests__/to-have-display-value.js
Expand Up @@ -21,36 +21,68 @@ 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', () => {
function mount() {
return 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>
`)
}

expect(queryByTestId('select')).toHaveDisplayValue(['Ananas', 'Avocado'])
expect(() =>
expect(queryByTestId('select')).not.toHaveDisplayValue([
it('matches only when all the multiple selected values are equal to all the expected values', () => {
const subject = mount()
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', () => {
const subject = mount()
expect(subject.queryByTestId('select')).toHaveDisplayValue([
'Avocado',
'Ananas',
])
})

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

test('it should work with input elements', () => {
Expand All @@ -59,6 +91,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 +105,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 numberOfMatchesWithValues = getNumberOfMatchesBetweenArrays(
values,
expectedValues,
)

const matchedWithAllValues = numberOfMatchesWithValues === values.length
const matchedWithAllExpectedValues =
numberOfMatchesWithValues === 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 getNumberOfMatchesBetweenArrays(arrayBase, array) {
return array.filter(
expected => arrayBase.filter(value => matches(value, expected)).length,
).length
}

0 comments on commit 5c9e8e5

Please sign in to comment.