-
Notifications
You must be signed in to change notification settings - Fork 4.3k
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
Modal: use code instead of keyCode for keyboard events #43429
Changes from all commits
b5bf690
8932e53
ec91a93
66b3898
45ba04e
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -6,7 +6,9 @@ import { | |
screen, | ||
fireEvent, | ||
waitForElementToBeRemoved, | ||
waitFor, | ||
} from '@testing-library/react'; | ||
import userEvent from '@testing-library/user-event'; | ||
|
||
/** | ||
* Internal dependencies | ||
|
@@ -78,6 +80,10 @@ describe( 'Confirm', () => { | |
} ); | ||
|
||
it( 'should not render if closed by clicking `OK`, and the `onConfirm` callback should be called', async () => { | ||
const user = userEvent.setup( { | ||
advanceTimers: jest.advanceTimersByTime, | ||
} ); | ||
|
||
const onConfirm = jest.fn().mockName( 'onConfirm()' ); | ||
|
||
render( | ||
|
@@ -89,13 +95,17 @@ describe( 'Confirm', () => { | |
const confirmDialog = screen.getByRole( 'dialog' ); | ||
const button = screen.getByText( 'OK' ); | ||
|
||
fireEvent.click( button ); | ||
await user.click( button ); | ||
|
||
expect( confirmDialog ).not.toBeInTheDocument(); | ||
expect( onConfirm ).toHaveBeenCalled(); | ||
} ); | ||
|
||
it( 'should not render if closed by clicking `Cancel`, and the `onCancel` callback should be called', async () => { | ||
const user = userEvent.setup( { | ||
advanceTimers: jest.advanceTimersByTime, | ||
} ); | ||
|
||
const onCancel = jest.fn().mockName( 'onCancel()' ); | ||
|
||
render( | ||
|
@@ -107,13 +117,17 @@ describe( 'Confirm', () => { | |
const confirmDialog = screen.getByRole( 'dialog' ); | ||
const button = screen.getByText( 'Cancel' ); | ||
|
||
fireEvent.click( button ); | ||
await user.click( button ); | ||
|
||
expect( confirmDialog ).not.toBeInTheDocument(); | ||
expect( onCancel ).toHaveBeenCalled(); | ||
} ); | ||
|
||
it( 'should be dismissable even if an `onCancel` callback is not provided', async () => { | ||
const user = userEvent.setup( { | ||
advanceTimers: jest.advanceTimersByTime, | ||
} ); | ||
|
||
render( | ||
<ConfirmDialog onConfirm={ noop }> | ||
Are you sure? | ||
|
@@ -123,7 +137,7 @@ describe( 'Confirm', () => { | |
const confirmDialog = screen.getByRole( 'dialog' ); | ||
const button = screen.getByText( 'Cancel' ); | ||
|
||
fireEvent.click( button ); | ||
await user.click( button ); | ||
|
||
expect( confirmDialog ).not.toBeInTheDocument(); | ||
} ); | ||
|
@@ -140,6 +154,7 @@ describe( 'Confirm', () => { | |
const confirmDialog = screen.getByRole( 'dialog' ); | ||
|
||
//The overlay click is handled by detecting an onBlur from the modal frame. | ||
// TODO: replace with `@testing-library/user-event` | ||
fireEvent.blur( confirmDialog ); | ||
|
||
await waitForElementToBeRemoved( confirmDialog ); | ||
|
@@ -149,6 +164,10 @@ describe( 'Confirm', () => { | |
} ); | ||
|
||
it( 'should not render if dialog is closed by pressing `Escape`, and the `onCancel` callback should be called', async () => { | ||
const user = userEvent.setup( { | ||
advanceTimers: jest.advanceTimersByTime, | ||
} ); | ||
|
||
const onCancel = jest.fn().mockName( 'onCancel()' ); | ||
|
||
render( | ||
|
@@ -159,13 +178,17 @@ describe( 'Confirm', () => { | |
|
||
const confirmDialog = screen.getByRole( 'dialog' ); | ||
|
||
fireEvent.keyDown( confirmDialog, { keyCode: 27 } ); | ||
await user.keyboard( '[Escape]' ); | ||
|
||
expect( confirmDialog ).not.toBeInTheDocument(); | ||
expect( onCancel ).toHaveBeenCalled(); | ||
} ); | ||
|
||
it( 'should not render if dialog is closed by pressing `Enter`, and the `onConfirm` callback should be called', async () => { | ||
const user = userEvent.setup( { | ||
advanceTimers: jest.advanceTimersByTime, | ||
} ); | ||
|
||
const onConfirm = jest.fn().mockName( 'onConfirm()' ); | ||
|
||
render( | ||
|
@@ -176,7 +199,7 @@ describe( 'Confirm', () => { | |
|
||
const confirmDialog = screen.getByRole( 'dialog' ); | ||
|
||
fireEvent.keyDown( confirmDialog, { keyCode: 13 } ); | ||
await user.keyboard( '[Enter]' ); | ||
|
||
expect( confirmDialog ).not.toBeInTheDocument(); | ||
expect( onConfirm ).toHaveBeenCalled(); | ||
|
@@ -220,6 +243,10 @@ describe( 'Confirm', () => { | |
} ); | ||
|
||
it( 'should call the `onConfirm` callback if `OK`', async () => { | ||
const user = userEvent.setup( { | ||
advanceTimers: jest.advanceTimersByTime, | ||
} ); | ||
|
||
const onConfirm = jest.fn().mockName( 'onConfirm()' ); | ||
|
||
render( | ||
|
@@ -230,12 +257,16 @@ describe( 'Confirm', () => { | |
|
||
const button = screen.getByText( 'OK' ); | ||
|
||
fireEvent.click( button ); | ||
await user.click( button ); | ||
|
||
expect( onConfirm ).toHaveBeenCalled(); | ||
} ); | ||
|
||
it( 'should call the `onCancel` callback if `Cancel` is clicked', async () => { | ||
const user = userEvent.setup( { | ||
advanceTimers: jest.advanceTimersByTime, | ||
} ); | ||
|
||
const onCancel = jest.fn().mockName( 'onCancel()' ); | ||
|
||
render( | ||
|
@@ -250,17 +281,15 @@ describe( 'Confirm', () => { | |
|
||
const button = screen.getByText( 'Cancel' ); | ||
|
||
fireEvent.click( button ); | ||
await user.click( button ); | ||
|
||
expect( onCancel ).toHaveBeenCalled(); | ||
} ); | ||
|
||
it( 'should call the `onCancel` callback if the overlay is clicked', async () => { | ||
jest.useFakeTimers(); | ||
|
||
const onCancel = jest.fn().mockName( 'onCancel()' ); | ||
|
||
const wrapper = render( | ||
render( | ||
<ConfirmDialog | ||
isOpen={ true } | ||
onConfirm={ noop } | ||
|
@@ -270,45 +299,43 @@ describe( 'Confirm', () => { | |
</ConfirmDialog> | ||
); | ||
|
||
const frame = wrapper.baseElement.querySelector( | ||
'.components-modal__frame' | ||
); | ||
const confirmDialog = screen.getByRole( 'dialog' ); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
|
||
//The overlay click is handled by detecting an onBlur from the modal frame. | ||
fireEvent.blur( frame ); | ||
|
||
// We don't wait for a DOM side effect here, so we need to fake the timers | ||
// and "advance" it so that the `queueBlurCheck` in the `useFocusOutside` hook | ||
// properly executes its timeout task. | ||
jest.advanceTimersByTime( 0 ); | ||
|
||
expect( onCancel ).toHaveBeenCalled(); | ||
// TODO: replace with `@testing-library/user-event` | ||
fireEvent.blur( confirmDialog ); | ||
|
||
jest.useRealTimers(); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. There wasn't any need for fake timers to start with |
||
// Wait for a DOM side effect here, so that the `queueBlurCheck` in the | ||
// `useFocusOutside` hook properly executes its timeout task. | ||
await waitFor( () => expect( onCancel ).toHaveBeenCalled() ); | ||
} ); | ||
|
||
it( 'should call the `onCancel` callback if the `Escape` key is pressed', async () => { | ||
const user = userEvent.setup( { | ||
advanceTimers: jest.advanceTimersByTime, | ||
} ); | ||
|
||
const onCancel = jest.fn().mockName( 'onCancel()' ); | ||
|
||
const wrapper = render( | ||
render( | ||
<ConfirmDialog onConfirm={ noop } onCancel={ onCancel }> | ||
Are you sure? | ||
</ConfirmDialog> | ||
); | ||
|
||
const frame = wrapper.baseElement.querySelector( | ||
'.components-modal__frame' | ||
); | ||
|
||
fireEvent.keyDown( frame, { keyCode: 27 } ); | ||
await user.keyboard( '[Escape]' ); | ||
|
||
expect( onCancel ).toHaveBeenCalled(); | ||
} ); | ||
|
||
it( 'should call the `onConfirm` callback if the `Enter` key is pressed', async () => { | ||
const user = userEvent.setup( { | ||
advanceTimers: jest.advanceTimersByTime, | ||
} ); | ||
|
||
const onConfirm = jest.fn().mockName( 'onConfirm()' ); | ||
|
||
const wrapper = render( | ||
render( | ||
<ConfirmDialog | ||
isOpen={ true } | ||
onConfirm={ onConfirm } | ||
|
@@ -318,11 +345,7 @@ describe( 'Confirm', () => { | |
</ConfirmDialog> | ||
); | ||
|
||
const frame = wrapper.baseElement.querySelector( | ||
'.components-modal__frame' | ||
); | ||
|
||
fireEvent.keyDown( frame, { keyCode: 13 } ); | ||
await user.keyboard( '[Enter]' ); | ||
|
||
expect( onConfirm ).toHaveBeenCalled(); | ||
} ); | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -26,25 +26,27 @@ describe( 'Modal', () => { | |
render( | ||
<Modal aria={ { labelledby: 'title-id' } }> | ||
{ /* eslint-disable-next-line no-restricted-syntax */ } | ||
<h1 id="title-id">Test Title</h1> | ||
<h1 id="title-id">Modal Title Text</h1> | ||
</Modal> | ||
); | ||
expect( screen.getByRole( 'dialog' ) ).toHaveAttribute( | ||
'aria-labelledby', | ||
'title-id' | ||
expect( screen.getByRole( 'dialog' ) ).toHaveAccessibleName( | ||
'Modal Title Text' | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The previous test was not actually testing for the computed accessible name |
||
); | ||
} ); | ||
|
||
it( 'prefers the aria label of the title prop over the aria.labelledby prop', () => { | ||
render( | ||
<Modal title="Test Title" aria={ { labelledby: 'title-id' } }> | ||
<Modal | ||
title="Modal Title Attribute" | ||
aria={ { labelledby: 'title-id' } } | ||
> | ||
{ /* eslint-disable-next-line no-restricted-syntax */ } | ||
<h1 id="title-id">Wrong Title</h1> | ||
<h1 id="title-id">Modal Title Text</h1> | ||
</Modal> | ||
); | ||
const dialog = screen.getByRole( 'dialog' ); | ||
const titleId = within( dialog ).getByText( 'Test Title' ).id; | ||
expect( dialog ).toHaveAttribute( 'aria-labelledby', titleId ); | ||
expect( screen.getByRole( 'dialog' ) ).toHaveAccessibleName( | ||
'Modal Title Attribute' | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Same here — the previous test was not actually testing for the computed accessible name |
||
); | ||
} ); | ||
|
||
it( 'hides the header when the `__experimentalHideHeader` prop is used', () => { | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I tried to simulate a click on
document.body
and even on the Modal's overlay, but that was not enough. I therefore decided to leavefireEvent
for now to unblock the rest of this PR (same for another test a few lines below)