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

fix: add custom element support to toBeDisabled #368

Merged
merged 4 commits into from
Feb 3, 2022
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
3 changes: 2 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -160,7 +160,8 @@ toBeDisabled()
This allows you to check whether an element is disabled from the user's
perspective. According to the specification, the following elements can be
[disabled](https://html.spec.whatwg.org/multipage/semantics-other.html#disabled-elements):
`button`, `input`, `select`, `textarea`, `optgroup`, `option`, `fieldset`.
`button`, `input`, `select`, `textarea`, `optgroup`, `option`, `fieldset`, and
custom elements.

This custom matcher considers an element as disabled if the element is among the
types of elements that can be disabled (listed above), and the `disabled`
Expand Down
42 changes: 42 additions & 0 deletions src/__tests__/to-be-disabled.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,13 @@
import document from './helpers/document'
import {render} from './helpers/test-utils'

const window = document.defaultView

window.customElements.define(
'custom-element',
class extends window.HTMLElement {},
)

test('.toBeDisabled', () => {
const {queryByTestId} = render(`
<div>
Expand Down Expand Up @@ -109,6 +117,23 @@ test('.toBeDisabled fieldset>legend', () => {
expect(queryByTestId('outer-fieldset-element')).toBeDisabled()
})

test('.toBeDisabled custom element', () => {
const {queryByTestId} = render(`
<custom-element data-testid="disabled-custom-element" disabled=""></custom-element>
<custom-element data-testid="enabled-custom-element"></custom-element>
`)

expect(queryByTestId('disabled-custom-element')).toBeDisabled()
expect(() => {
expect(queryByTestId('disabled-custom-element')).not.toBeDisabled()
}).toThrowError('element is disabled')

expect(queryByTestId('enabled-custom-element')).not.toBeDisabled()
Copy link
Contributor Author

Choose a reason for hiding this comment

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

This currently fails with:

Screen Shot 2021-06-04 at 1 16 52 PM

I can't figure this out: clearly the custom element is not disabled. More to the point, if I remove the disabled custom element (L122) long with the corresponding assertions (L126-129), the test now passes. It's as if some state was not being reset or something. @gnapse, do you have any idea?

(The same thing happens on the other test L281-L284)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

In other words, splitting the 2 new tests into 4 tests (see diff below) passes. I could do that, but I would prefer to figure out first if this is a bug with the test or with the code: if I split them and it passes, nothing ensures this wouldn't happen in real-life tests that have a mix of disabled and non-disabled custom elements.

Diff
@ src/__tests__/to-be-disabled.js:120 @ test('.toBeDisabled fieldset>legend', () => {
   expect(queryByTestId('outer-fieldset-element')).toBeDisabled()
 })

-test('.toBeDisabled custom element', () => {
+test('.toBeDisabled disabled custom element', () => {
   const {queryByTestId} = render(`
-    <custom-element data-testid="disabled-custom-element" disabled="" />
-    <custom-element data-testid="enabled-custom-element" />
+    <custom-element data-testid="custom-element" disabled="" />
   `)

-  expect(queryByTestId('disabled-custom-element')).toBeDisabled()
+  expect(queryByTestId('custom-element')).toBeDisabled()
   expect(() => {
-    expect(queryByTestId('disabled-custom-element')).not.toBeDisabled()
+    expect(queryByTestId('custom-element')).not.toBeDisabled()
   }).toThrowError('element is disabled')
+})
+
+test('.toBeDisabled enabled custom element', () => {
+  const {queryByTestId} = render(`
+    <custom-element data-testid="custom-element" />
+  `)

-  expect(queryByTestId('enabled-custom-element')).not.toBeDisabled()
+  expect(queryByTestId('custom-element')).not.toBeDisabled()
   expect(() => {
-    expect(queryByTestId('enabled-custom-element')).toBeDisabled()
+    expect(queryByTestId('custom-element')).toBeDisabled()
   }).toThrowError('element is not disabled')
 })

@ src/__tests__/to-be-disabled.js:275 @ test('.toBeEnabled fieldset>legend', () => {
   }).toThrowError()
 })

-test('.toBeEnabled custom element', () => {
+test('.toBeEnabled disabled custom element', () => {
   const {queryByTestId} = render(`
-    <custom-element data-testid="disabled-custom-element" disabled="" />
-    <custom-element data-testid="enabled-custom-element" />
+    <custom-element data-testid="custom-element" disabled="" />
   `)

-  expect(queryByTestId('disabled-custom-element')).not.toBeEnabled()
+  expect(queryByTestId('custom-element')).not.toBeEnabled()
   expect(() => {
-    expect(queryByTestId('disabled-custom-element')).toBeEnabled()
+    expect(queryByTestId('custom-element')).toBeEnabled()
   }).toThrowError('element is not enabled')
+})
+
+test('.toBeEnabled enabled custom element', () => {
+  const {queryByTestId} = render(`
+    <custom-element data-testid="custom-element" />
+  `)

-  expect(queryByTestId('enabled-custom-element')).toBeEnabled()
+  expect(queryByTestId('custom-element')).toBeEnabled()
   expect(() => {
-    expect(queryByTestId('enabled-custom-element')).not.toBeEnabled()
+    expect(queryByTestId('custom-element')).not.toBeEnabled()
   }).toThrowError('element is enabled')
 })

Copy link
Contributor Author

Choose a reason for hiding this comment

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

BTW I am not wrapping the elements in a div/fragment because the render function does it on its own, but the error is the same if I do.

expect(() => {
expect(queryByTestId('enabled-custom-element')).toBeDisabled()
}).toThrowError('element is not disabled')
})

test('.toBeEnabled', () => {
const {queryByTestId} = render(`
<div>
Expand Down Expand Up @@ -241,3 +266,20 @@ test('.toBeEnabled fieldset>legend', () => {
expect(queryByTestId('outer-fieldset-element')).toBeEnabled()
}).toThrowError()
})

test('.toBeEnabled custom element', () => {
const {queryByTestId} = render(`
<custom-element data-testid="disabled-custom-element" disabled=""></custom-element>
<custom-element data-testid="enabled-custom-element"></custom-element>
`)

expect(queryByTestId('disabled-custom-element')).not.toBeEnabled()
expect(() => {
expect(queryByTestId('disabled-custom-element')).toBeEnabled()
}).toThrowError('element is not enabled')

expect(queryByTestId('enabled-custom-element')).toBeEnabled()
expect(() => {
expect(queryByTestId('enabled-custom-element')).not.toBeEnabled()
}).toThrowError('element is enabled')
})
11 changes: 10 additions & 1 deletion src/to-be-disabled.js
Original file line number Diff line number Diff line change
Expand Up @@ -36,8 +36,17 @@ function isElementDisabledByParent(element, parent) {
)
}

function isCustomElement(tag) {
return tag.includes('-')
}

/*
* Only certain form elements and custom elements can actually be disabled:
* https://html.spec.whatwg.org/multipage/semantics-other.html#disabled-elements
*/
function canElementBeDisabled(element) {
return FORM_TAGS.includes(getTag(element))
const tag = getTag(element)
return FORM_TAGS.includes(tag) || isCustomElement(tag)
}

function isElementDisabled(element) {
Expand Down