-
Notifications
You must be signed in to change notification settings - Fork 251
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: add userEvent.keyboard API (#581)
BREAKING CHANGE: `userEvent.type` features a rewritten implementation shared with the new `userEvent.keyboard`. This might break code depending on unintended/undocumented behavior of the previous implementation. BREAKING CHANGE: `userEvent.type` treats `{` and `[` as special characters. BREAKING CHANGE: `userEvent.type` returns no Promise if called without `delay`.
- Loading branch information
1 parent
e83d949
commit f251d15
Showing
60 changed files
with
2,394 additions
and
1,317 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
module.exports = { | ||
extends: './node_modules/kcd-scripts/eslint.js', | ||
settings: { | ||
'import/resolver': { | ||
node: { | ||
extensions: ['.js', '.ts'], | ||
}, | ||
}, | ||
}, | ||
rules: { | ||
'testing-library/no-dom-import': 0, | ||
'@typescript-eslint/non-nullable-type-assertion-style': 0, | ||
}, | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,78 @@ | ||
import cases from 'jest-in-case' | ||
import {getNextKeyDef} from 'keyboard/getNextKeyDef' | ||
import {defaultKeyMap} from 'keyboard/keyMap' | ||
import {keyboardKey, keyboardOptions} from 'keyboard/types' | ||
|
||
const options: keyboardOptions = { | ||
document, | ||
keyboardMap: defaultKeyMap, | ||
autoModify: false, | ||
delay: 123, | ||
} | ||
|
||
cases( | ||
'reference key per', | ||
({text, key, code}) => { | ||
expect(getNextKeyDef(`${text}foo`, options)).toEqual( | ||
expect.objectContaining({ | ||
keyDef: expect.objectContaining({ | ||
key, | ||
code, | ||
}) as keyboardKey, | ||
}), | ||
) | ||
}, | ||
{ | ||
code: {text: '[ControlLeft]', key: 'Control', code: 'ControlLeft'}, | ||
'unimplemented code': {text: '[Foo]', key: 'Unknown', code: 'Foo'}, | ||
key: {text: '{Control}', key: 'Control', code: 'ControlLeft'}, | ||
'unimplemented key': {text: '{Foo}', key: 'Foo', code: 'Unknown'}, | ||
'legacy modifier': {text: '{ctrl}', key: 'Control', code: 'ControlLeft'}, | ||
'printable character': {text: 'a', key: 'a', code: 'KeyA'}, | ||
'{ as printable': {text: '{{', key: '{', code: 'Unknown'}, | ||
'[ as printable': {text: '[[', key: '[', code: 'Unknown'}, | ||
}, | ||
) | ||
|
||
cases( | ||
'modifiers', | ||
({text, modifiers}) => { | ||
expect(getNextKeyDef(`${text}foo`, options)).toEqual( | ||
expect.objectContaining(modifiers), | ||
) | ||
}, | ||
{ | ||
'no releasePrevious': { | ||
text: '{Control}', | ||
modifiers: {releasePrevious: false}, | ||
}, | ||
'releasePrevious per key': { | ||
text: '{/Control}', | ||
modifiers: {releasePrevious: true}, | ||
}, | ||
'releasePrevious per code': { | ||
text: '[/ControlLeft]', | ||
modifiers: {releasePrevious: true}, | ||
}, | ||
'default releaseSelf': { | ||
text: '{Control}', | ||
modifiers: {releaseSelf: true}, | ||
}, | ||
'keep key pressed per key': { | ||
text: '{Control>}', | ||
modifiers: {releaseSelf: false}, | ||
}, | ||
'keep key pressed per code': { | ||
text: '[Control>]', | ||
modifiers: {releaseSelf: false}, | ||
}, | ||
'no releaseSelf on legacy modifier': { | ||
text: '{ctrl}', | ||
modifiers: {releaseSelf: false}, | ||
}, | ||
'release legacy modifier': { | ||
text: '{ctrl/}', | ||
modifiers: {releaseSelf: true}, | ||
}, | ||
}, | ||
) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,137 @@ | ||
import userEvent from '../../index' | ||
import {addListeners, setup} from '../helpers/utils' | ||
|
||
it('type without focus', () => { | ||
const {element} = setup('<input/>') | ||
const {getEventSnapshot} = addListeners(document.body) | ||
|
||
userEvent.keyboard('foo') | ||
|
||
expect(element).toHaveValue('') | ||
expect(getEventSnapshot()).toMatchInlineSnapshot(` | ||
Events fired on: body | ||
body - keydown: f (102) | ||
body - keypress: f (102) | ||
body - keyup: f (102) | ||
body - keydown: o (111) | ||
body - keypress: o (111) | ||
body - keyup: o (111) | ||
body - keydown: o (111) | ||
body - keypress: o (111) | ||
body - keyup: o (111) | ||
`) | ||
}) | ||
|
||
it('type with focus', () => { | ||
const {element} = setup('<input/>') | ||
const {getEventSnapshot} = addListeners(document.body) | ||
;(element as HTMLInputElement).focus() | ||
|
||
userEvent.keyboard('foo') | ||
|
||
expect(element).toHaveValue('foo') | ||
expect(getEventSnapshot()).toMatchInlineSnapshot(` | ||
Events fired on: body | ||
input[value=""] - focusin | ||
input[value=""] - keydown: f (102) | ||
input[value=""] - keypress: f (102) | ||
input[value="f"] - input | ||
input[value="f"] - keyup: f (102) | ||
input[value="f"] - keydown: o (111) | ||
input[value="f"] - keypress: o (111) | ||
input[value="fo"] - input | ||
input[value="fo"] - keyup: o (111) | ||
input[value="fo"] - keydown: o (111) | ||
input[value="fo"] - keypress: o (111) | ||
input[value="foo"] - input | ||
input[value="foo"] - keyup: o (111) | ||
`) | ||
}) | ||
|
||
it('type asynchronous', async () => { | ||
const {element} = setup('<input/>') | ||
const {getEventSnapshot} = addListeners(document.body) | ||
;(element as HTMLInputElement).focus() | ||
|
||
// eslint-disable-next-line testing-library/no-await-sync-events | ||
await userEvent.keyboard('foo', {delay: 1}) | ||
|
||
expect(element).toHaveValue('foo') | ||
expect(getEventSnapshot()).toMatchInlineSnapshot(` | ||
Events fired on: body | ||
input[value=""] - focusin | ||
input[value=""] - keydown: f (102) | ||
input[value=""] - keypress: f (102) | ||
input[value="f"] - input | ||
input[value="f"] - keyup: f (102) | ||
input[value="f"] - keydown: o (111) | ||
input[value="f"] - keypress: o (111) | ||
input[value="fo"] - input | ||
input[value="fo"] - keyup: o (111) | ||
input[value="fo"] - keydown: o (111) | ||
input[value="fo"] - keypress: o (111) | ||
input[value="foo"] - input | ||
input[value="foo"] - keyup: o (111) | ||
`) | ||
}) | ||
|
||
describe('error', () => { | ||
afterEach(() => { | ||
;(console.error as jest.MockedFunction<typeof console.error>).mockClear() | ||
}) | ||
|
||
it('error in sync', async () => { | ||
const err = jest.spyOn(console, 'error') | ||
err.mockImplementation(() => {}) | ||
|
||
userEvent.keyboard('{!') | ||
|
||
// the catch will be asynchronous | ||
await Promise.resolve() | ||
|
||
expect(err).toHaveBeenCalledWith(expect.any(Error)) | ||
expect(err.mock.calls[0][0]).toHaveProperty( | ||
'message', | ||
'Expected key descriptor but found "!" in "{!"', | ||
) | ||
}) | ||
|
||
it('error in async', async () => { | ||
const promise = userEvent.keyboard('{!', {delay: 1}) | ||
|
||
return expect(promise).rejects.toThrowError( | ||
'Expected key descriptor but found "!" in "{!"', | ||
) | ||
}) | ||
}) | ||
|
||
it('continue typing with state', () => { | ||
const {element, getEventSnapshot, clearEventCalls} = setup('<input/>') | ||
;(element as HTMLInputElement).focus() | ||
clearEventCalls() | ||
|
||
const state = userEvent.keyboard('[ShiftRight>]') | ||
|
||
expect(getEventSnapshot()).toMatchInlineSnapshot(` | ||
Events fired on: input[value=""] | ||
input[value=""] - keydown: Shift (16) {shift} | ||
`) | ||
clearEventCalls() | ||
|
||
userEvent.keyboard('F[/ShiftRight]', {keyboardState: state}) | ||
|
||
expect(getEventSnapshot()).toMatchInlineSnapshot(` | ||
Events fired on: input[value="F"] | ||
input[value=""] - keydown: F (70) {shift} | ||
input[value=""] - keypress: F (70) {shift} | ||
input[value="F"] - input | ||
"{CURSOR}" -> "F{CURSOR}" | ||
input[value="F"] - keyup: F (70) {shift} | ||
input[value="F"] - keyup: Shift (16) | ||
`) | ||
}) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.