Skip to content

feat: toBeValid supports <form/> element #127

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

Merged
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
47 changes: 39 additions & 8 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -260,13 +260,14 @@ expect(
toBeInvalid()
```

This allows you to check if an form element is currently invalid.
This allows you to check if a form element, or the entire `form`, is currently
invalid.

An element is invalid if it is having an
An `input`, `select`, `textarea`, or `form` element is invalid if it has an
[`aria-invalid` attribute](https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/ARIA_Techniques/Using_the_aria-invalid_attribute)
or if the result of
with no value or a value of `"true"`, or if the result of
[`checkValidity()`](https://developer.mozilla.org/en-US/docs/Web/Guide/HTML/HTML5/Constraint_validation)
are false.
is `false`.

#### Examples

Expand All @@ -275,6 +276,14 @@ are false.
<input data-testid="aria-invalid" aria-invalid />
<input data-testid="aria-invalid-value" aria-invalid="true" />
<input data-testid="aria-invalid-false" aria-invalid="false" />

<form data-testid="valid-form">
<input />
</form>

<form data-testid="invalid-form">
<input required />
</form>
```

##### Using document.querySelector
Expand All @@ -284,6 +293,9 @@ expect(queryByTestId('no-aria-invalid')).not.toBeInvalid()
expect(queryByTestId('aria-invalid')).toBeInvalid()
expect(queryByTestId('aria-invalid-value')).toBeInvalid()
expect(queryByTestId('aria-invalid-false')).not.toBeInvalid()

expect(queryByTestId('valid-form')).not.toBeInvalid()
expect(queryByTestId('invalid-form')).toBeInvalid()
```

##### Using DOM Testing Library
Expand All @@ -293,6 +305,9 @@ expect(getByTestId(container, 'no-aria-invalid')).not.toBeInvalid()
expect(getByTestId(container, 'aria-invalid')).toBeInvalid()
expect(getByTestId(container, 'aria-invalid-value')).toBeInvalid()
expect(getByTestId(container, 'aria-invalid-false')).not.toBeInvalid()

expect(getByTestId(container, 'valid-form')).not.toBeInvalid()
expect(getByTestId(container, 'invalid-form')).toBeInvalid()
```

<hr />
Expand Down Expand Up @@ -372,12 +387,14 @@ expect(getByTestId(container, 'supported-role-aria')).toBeRequired()
toBeValid()
```

This allows you to check if the value of a form element is currently valid.
This allows you to check if the value of a form element, or the entire `form`,
is currently valid.

An element is valid if it is not having an
An `input`, `select`, `textarea`, or `form` element is valid if it has no
[`aria-invalid` attribute](https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/ARIA_Techniques/Using_the_aria-invalid_attribute)
or having `false` as a value and returning `true` when calling
[`checkValidity()`](https://developer.mozilla.org/en-US/docs/Web/Guide/HTML/HTML5/Constraint_validation).
or an attribute value of `"false"`. The result of
[`checkValidity()`](https://developer.mozilla.org/en-US/docs/Web/Guide/HTML/HTML5/Constraint_validation)
must also be `true`.

#### Examples

Expand All @@ -386,6 +403,14 @@ or having `false` as a value and returning `true` when calling
<input data-testid="aria-invalid" aria-invalid />
<input data-testid="aria-invalid-value" aria-invalid="true" />
<input data-testid="aria-invalid-false" aria-invalid="false" />

<form data-testid="valid-form">
<input />
</form>

<form data-testid="invalid-form">
<input required />
</form>
```

##### Using document.querySelector
Expand All @@ -395,6 +420,9 @@ expect(queryByTestId('no-aria-invalid')).toBeValid()
expect(queryByTestId('aria-invalid')).not.toBeValid()
expect(queryByTestId('aria-invalid-value')).not.toBeValid()
expect(queryByTestId('aria-invalid-false')).toBeValid()

expect(queryByTestId('valid-form')).toBeValid()
expect(queryByTestId('invalid-form')).not.toBeValid()
```

##### Using DOM Testing Library
Expand All @@ -404,6 +432,9 @@ expect(getByTestId(container, 'no-aria-invalid')).toBeValid()
expect(getByTestId(container, 'aria-invalid')).not.toBeValid()
expect(getByTestId(container, 'aria-invalid-value')).not.toBeValid()
expect(getByTestId(container, 'aria-invalid-false')).toBeValid()

expect(getByTestId(container, 'valid-form')).toBeValid()
expect(getByTestId(container, 'invalid-form')).not.toBeValid()
```

<hr />
Expand Down
172 changes: 106 additions & 66 deletions src/__tests__/to-be-invalid.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,74 +19,114 @@ import {render} from './helpers/test-utils'
* @link https://developer.mozilla.org/en-US/docs/Web/Guide/HTML/HTML5/Constraint_validation
* @link https://github.com/jsdom/jsdom
*/
function getDOMInput(htmlString) {
const dom = new JSDOM(htmlString)

return dom.window.document.querySelector('input')
function getDOMElement(htmlString, selector) {
return new JSDOM(htmlString).window.document.querySelector(selector)
}

const invalidInputNode = getDOMInput(
`<input pattern="[a-z]{1,3}" value="test+">`,
)

test('.toBeInvalid', () => {
const {queryByTestId} = render(`
<div>
<input data-testid="no-aria-invalid">
<input data-testid="aria-invalid" aria-invalid>
<input data-testid="aria-invalid-value" aria-invalid="true">
<input data-testid="aria-invalid-false" aria-invalid="false">
</div>
`)

expect(queryByTestId('no-aria-invalid')).not.toBeInvalid()
expect(queryByTestId('aria-invalid')).toBeInvalid()
expect(queryByTestId('aria-invalid-value')).toBeInvalid()
expect(queryByTestId('aria-invalid-false')).not.toBeInvalid()
expect(invalidInputNode).toBeInvalid()

// negative test cases wrapped in throwError assertions for coverage.
expect(() =>
expect(queryByTestId('no-aria-invalid')).toBeInvalid(),
).toThrowError()
expect(() =>
expect(queryByTestId('aria-invalid')).not.toBeInvalid(),
).toThrowError()
expect(() =>
expect(queryByTestId('aria-invalid-value')).not.toBeInvalid(),
).toThrowError()
expect(() =>
expect(queryByTestId('aria-invalid-false')).toBeInvalid(),
).toThrowError()
expect(() => expect(invalidInputNode).not.toBeInvalid()).toThrowError()
// A required field without a value is invalid
const invalidInputHtml = `<input required>`

const invalidInputNode = getDOMElement(invalidInputHtml, 'input')

// A form is invalid if it contains an invalid input
const invalidFormHtml = `<form>${invalidInputHtml}</form>`

const invalidFormNode = getDOMElement(invalidFormHtml, 'form')

describe('.toBeInvalid', () => {
test('handles <input/>', () => {
const {queryByTestId} = render(`
<div>
<input data-testid="no-aria-invalid">
<input data-testid="aria-invalid" aria-invalid>
<input data-testid="aria-invalid-value" aria-invalid="true">
<input data-testid="aria-invalid-false" aria-invalid="false">
</div>
`)

expect(queryByTestId('no-aria-invalid')).not.toBeInvalid()
expect(queryByTestId('aria-invalid')).toBeInvalid()
expect(queryByTestId('aria-invalid-value')).toBeInvalid()
expect(queryByTestId('aria-invalid-false')).not.toBeInvalid()
expect(invalidInputNode).toBeInvalid()

// negative test cases wrapped in throwError assertions for coverage.
expect(() =>
expect(queryByTestId('no-aria-invalid')).toBeInvalid(),
).toThrowError()
expect(() =>
expect(queryByTestId('aria-invalid')).not.toBeInvalid(),
).toThrowError()
expect(() =>
expect(queryByTestId('aria-invalid-value')).not.toBeInvalid(),
).toThrowError()
expect(() =>
expect(queryByTestId('aria-invalid-false')).toBeInvalid(),
).toThrowError()
expect(() => expect(invalidInputNode).not.toBeInvalid()).toThrowError()
})

test('handles <form/>', () => {
const {queryByTestId} = render(`
<form data-testid="valid">
<input>
</form>
`)

expect(queryByTestId('valid')).not.toBeInvalid()
expect(invalidFormNode).toBeInvalid()

// negative test cases wrapped in throwError assertions for coverage.
expect(() => expect(queryByTestId('valid')).toBeInvalid()).toThrowError()
expect(() => expect(invalidFormNode).not.toBeInvalid()).toThrowError()
})
})

test('.toBeValid', () => {
const {queryByTestId} = render(`
<div>
<input data-testid="no-aria-invalid">
<input data-testid="aria-invalid" aria-invalid>
<input data-testid="aria-invalid-value" aria-invalid="true">
<input data-testid="aria-invalid-false" aria-invalid="false">
</div>
`)

expect(queryByTestId('no-aria-invalid')).toBeValid()
expect(queryByTestId('aria-invalid')).not.toBeValid()
expect(queryByTestId('aria-invalid-value')).not.toBeValid()
expect(queryByTestId('aria-invalid-false')).toBeValid()
expect(invalidInputNode).not.toBeValid()

// negative test cases wrapped in throwError assertions for coverage.
expect(() =>
expect(queryByTestId('no-aria-invalid')).not.toBeValid(),
).toThrowError()
expect(() => expect(queryByTestId('aria-invalid')).toBeValid()).toThrowError()
expect(() =>
expect(queryByTestId('aria-invalid-value')).toBeValid(),
).toThrowError()
expect(() =>
expect(queryByTestId('aria-invalid-false')).not.toBeValid(),
).toThrowError()
expect(() => expect(invalidInputNode).toBeValid()).toThrowError()
describe('.toBeValid', () => {
test('handles <input/>', () => {
const {queryByTestId} = render(`
<div>
<input data-testid="no-aria-invalid">
<input data-testid="aria-invalid" aria-invalid>
<input data-testid="aria-invalid-value" aria-invalid="true">
<input data-testid="aria-invalid-false" aria-invalid="false">
</div>
`)

expect(queryByTestId('no-aria-invalid')).toBeValid()
expect(queryByTestId('aria-invalid')).not.toBeValid()
expect(queryByTestId('aria-invalid-value')).not.toBeValid()
expect(queryByTestId('aria-invalid-false')).toBeValid()
expect(invalidInputNode).not.toBeValid()

// negative test cases wrapped in throwError assertions for coverage.
expect(() =>
expect(queryByTestId('no-aria-invalid')).not.toBeValid(),
).toThrowError()
expect(() =>
expect(queryByTestId('aria-invalid')).toBeValid(),
).toThrowError()
expect(() =>
expect(queryByTestId('aria-invalid-value')).toBeValid(),
).toThrowError()
expect(() =>
expect(queryByTestId('aria-invalid-false')).not.toBeValid(),
).toThrowError()
expect(() => expect(invalidInputNode).toBeValid()).toThrowError()
})

test('handles <form/>', () => {
const {queryByTestId} = render(`
<form data-testid="valid">
<input>
</form>
`)

expect(queryByTestId('valid')).toBeValid()
expect(invalidFormNode).not.toBeValid()

// negative test cases wrapped in throwError assertions for coverage.
expect(() => expect(queryByTestId('valid')).not.toBeValid()).toThrowError()
expect(() => expect(invalidFormNode).toBeValid()).toThrowError()
})
})
2 changes: 1 addition & 1 deletion src/to-be-invalid.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import {matcherHint, printReceived} from 'jest-matcher-utils'
import {checkHtmlElement, getTag} from './utils'

const FORM_TAGS = ['input', 'select', 'textarea']
const FORM_TAGS = ['form', 'input', 'select', 'textarea']

function isElementHavingAriaInvalid(element) {
return (
Expand Down