Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
61 changes: 59 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@ clear to read and to maintain.
- [`toHaveStyle`](#tohavestyle)
- [`toHaveTextContent`](#tohavetextcontent)
- [`toHaveValue`](#tohavevalue)
- [`toHaveDisplayValue`](#tohavedisplayvalue)
- [`toBeChecked`](#tobechecked)
- [Deprecated matchers](#deprecated-matchers)
- [`toBeInTheDOM`](#tobeinthedom)
Expand Down Expand Up @@ -732,8 +733,8 @@ toHaveValue(value: string | string[] | number)

This allows you to check whether the given form element has the specified value.
It accepts `<input>`, `<select>` and `<textarea>` elements with the exception of
of `<input type="checkbox">` and `<input type="radio">`, which can be
meaningfully matched only using [`toBeChecked`](#tobechecked) or
`<input type="checkbox">` and `<input type="radio">`, which can be meaningfully
matched only using [`toBeChecked`](#tobechecked) or
[`toHaveFormValues`](#tohaveformvalues).

For all other form elements, the value is matched using the same algorithm as in
Expand Down Expand Up @@ -768,6 +769,61 @@ expect(selectInput).not.toHaveValue(['second', 'third'])

<hr />

### `toHaveDisplayValue`

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

This allows you to check whether the given form element has the specified
displayed value (the one the end user will see). It accepts `<input>`,
`<select>` and `<textarea>` elements with the exception of
`<input type="checkbox">` and `<input type="radio">`, which can be meaningfully
matched only using [`toBeChecked`](#tobechecked) or
[`toHaveFormValues`](#tohaveformvalues).

#### Examples

```html
<label for="input-example">First name</label>
<input type="text" id="input-example" value="Luca" />

<label for="textarea-example">Description</label>
<textarea id="textarea-example">An example description here.</textarea>

<label for="single-select-example">Fruit</label>
<select id="single-select-example">
<option value="">Select a fruit...</option>
<option value="banana">Banana</option>
<option value="ananas">Ananas</option>
<option value="avocado">Avocado</option>
</select>

<label for="mutiple-select-example">Fruits</label>
<select id="multiple-select-example" multiple>
<option value="">Select a fruit...</option>
<option value="banana" selected>Banana</option>
<option value="ananas">Ananas</option>
<option value="avocado" selected>Avocado</option>
</select>
```

##### Using DOM Testing Library

```javascript
const input = screen.getByLabelText('First name')
const textarea = screen.getByLabelText('Description')
const selectSingle = screen.getByLabelText('Fruit')
const selectMultiple = screen.getByLabelText('Fruits')

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

<hr />

### `toBeChecked`

```typescript
Expand Down Expand Up @@ -959,6 +1015,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
119 changes: 119 additions & 0 deletions src/__tests__/to-have-display-value.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
import {render} from './helpers/test-utils'

test('it should work as expected', () => {
const {queryByTestId} = render(`
<select id="fruits" data-testid="select">
<option value="">Select a fruit...</option>
<option value="ananas">Ananas</option>
<option value="banana">Banana</option>
<option value="avocado">Avocado</option>
</select>
`)

expect(queryByTestId('select')).toHaveDisplayValue('Select a fruit...')
expect(queryByTestId('select')).not.toHaveDisplayValue('Banana')
Comment on lines +13 to +14
Copy link
Member

Choose a reason for hiding this comment

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

Would be nice if the test also covered the case of changing the selected option programmatically to banana and then check that toHaveDisplayValue('Banana') pass.

And maybe do it for the other cases as well (textarea and input).

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done, I'm wondering why the repository is not using fireEvent from the testing-library core (or even userEvent) 🤔

expect(() =>
expect(queryByTestId('select')).not.toHaveDisplayValue('Select a fruit...'),
).toThrow()
expect(() =>
expect(queryByTestId('select')).toHaveDisplayValue('Ananas'),
).toThrow()

queryByTestId('select').value = 'banana'
expect(queryByTestId('select')).toHaveDisplayValue('Banana')
})

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>
`)

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

expect(queryByTestId('select')).not.toHaveDisplayValue('Ananas')
expect(() =>
expect(queryByTestId('select')).toHaveDisplayValue('Ananas'),
).toThrow()

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

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

test('it should work with input elements', () => {
const {queryByTestId} = render(`
<input type="text" data-testid="input" value="Luca" />
`)

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

queryByTestId('input').value = 'Piero'
expect(queryByTestId('input')).toHaveDisplayValue('Piero')
})

test('it should work with textarea elements', () => {
const {queryByTestId} = render(
'<textarea data-testid="textarea-example">An example description here.</textarea>',
)

expect(queryByTestId('textarea-example')).toHaveDisplayValue(
'An example description here.',
)

queryByTestId('textarea-example').value = 'Another example'
expect(queryByTestId('textarea-example')).toHaveDisplayValue(
'Another example',
)
})

test('it should throw if element is not valid', () => {
const {queryByTestId} = render(`
<div data-testid="div">Banana</div>
<input type="radio" data-testid="radio" value="Something" />
<input type="checkbox" data-testid="checkbox" />
`)

let errorMessage
try {
expect(queryByTestId('div')).toHaveDisplayValue('Banana')
} catch (err) {
errorMessage = err.message
}

expect(errorMessage).toMatchInlineSnapshot(
`".toHaveDisplayValue() currently supports only input, textarea or select elements, try with another matcher instead."`,
)

try {
expect(queryByTestId('radio')).toHaveDisplayValue('Something')
} catch (err) {
errorMessage = err.message
}

expect(errorMessage).toMatchInlineSnapshot(
`".toHaveDisplayValue() currently does not support input[type=\\"radio\\"], try with another matcher instead."`,
)

try {
expect(queryByTestId('checkbox')).toHaveDisplayValue(true)
} catch (err) {
errorMessage = err.message
}

expect(errorMessage).toMatchInlineSnapshot(
`".toHaveDisplayValue() currently does not support input[type=\\"checkbox\\"], try with another matcher instead."`,
)
})
2 changes: 2 additions & 0 deletions src/matchers.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import {toBeDisabled, toBeEnabled} from './to-be-disabled'
import {toBeRequired} from './to-be-required'
import {toBeInvalid, toBeValid} from './to-be-invalid'
import {toHaveValue} from './to-have-value'
import {toHaveDisplayValue} from './to-have-display-value'
import {toBeChecked} from './to-be-checked'

export {
Expand All @@ -35,5 +36,6 @@ export {
toBeInvalid,
toBeValid,
toHaveValue,
toHaveDisplayValue,
toBeChecked,
}
44 changes: 44 additions & 0 deletions src/to-have-display-value.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
import {matcherHint} from 'jest-matcher-utils'

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

export function toHaveDisplayValue(htmlElement, expectedValue) {
checkHtmlElement(htmlElement, toHaveDisplayValue, this)
const tagName = htmlElement.tagName.toLowerCase()

if (!['select', 'input', 'textarea'].includes(tagName)) {
throw new Error(
'.toHaveDisplayValue() currently supports only input, textarea or select elements, try with another matcher instead.',
)
}

if (tagName === 'input' && ['radio', 'checkbox'].includes(htmlElement.type)) {
throw new Error(
`.toHaveDisplayValue() currently does not support input[type="${htmlElement.type}"], try with another matcher instead.`,
)
}

const value =
tagName === 'select'
? Array.from(htmlElement)
.filter(option => option.selected)
.map(option => option.textContent)
.toString()
: htmlElement.value

return {
pass: value === expectedValue.toString(),
message: () =>
getMessage(
matcherHint(
`${this.isNot ? '.not' : ''}.toHaveDisplayValue`,
'element',
'',
),
`Expected element ${this.isNot ? 'not ' : ''}to have display value`,
expectedValue,
'Received',
value,
),
}
}