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

feat: Add toHaveDisplayValue() matcher #223

Merged
merged 13 commits into from Apr 9, 2020
53 changes: 53 additions & 0 deletions README.md
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 @@ -768,6 +769,57 @@ expect(selectInput).not.toHaveValue(['second', 'third'])

<hr />

### `toHaveDisplayValue`

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

This allows you to check whether the given `<select>` element has the specified
value.
cloud-walker marked this conversation as resolved.
Show resolved Hide resolved

#### 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">
cloud-walker marked this conversation as resolved.
Show resolved Hide resolved
<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 inputSelect = screen.getByLabelText('Fruit')
const inputMultiple = screen.getByLabelText('Fruits')
cloud-walker marked this conversation as resolved.
Show resolved Hide resolved

expect(input).toHaveDisplayValue('Luca')
expect(textarea).toHaveDisplayValue('An example description here.')
expect(inputSelect).toHaveDisplayValue('Select a fruit...')
expect(inputMultiple).toHaveDisplayValue(['Banana', 'Avocado'])
cloud-walker marked this conversation as resolved.
Show resolved Hide resolved
```

<hr />

### `toBeChecked`

```typescript
Expand Down Expand Up @@ -959,6 +1011,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
102 changes: 102 additions & 0 deletions src/__tests__/to-have-display-value.js
@@ -0,0 +1,102 @@
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()
})

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()
})

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

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

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.',
)
})

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
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
@@ -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(),
gnapse marked this conversation as resolved.
Show resolved Hide resolved
message: () =>
getMessage(
matcherHint(
`${this.isNot ? '.not' : ''}.toHaveDisplayValue`,
'element',
'',
),
`Expected element ${this.isNot ? 'not ' : ''}to have display value`,
expectedValue,
'Received',
value,
),
}
}