We've got multiple open issues regarding the implementation of userEvent.type.
And while a few people offered help on this, I've got the impression we're a bit lost regarding what the code should do and look like in general as well as how and where each smaller problem should be addressed.
I propose we collect those issues, discuss what we want to achieve and refactor and comment the code so that we get more confidence working on this.
keyEvent.code #456 Should I have overlooked something, please mention it and I'll add it to the list.
What do people expect userEvent.type to do? What should it do?
While userEvent.click is very clear and intuitive to use as userEvent click this element!,
I guess userEvent.type started the same way userEvent trigger the key events to add to the value of this element!,
but I think most people expect it to be more userEvent press those buttons on the keyboard!.
I think it should resolve around document.getSelection() and userEvent.type(null, "a{del}{shift}b{tab}c{/shift}[Semicolon]d{selectall}") should be a valid expression to test handling of hot keys and similar.
One problem that comes to my mind is how to handle the keypress events as they are triggered multiple times as long as the button is pressed. Maybe a configurable random timeout between key events? So that even an expression for Press [A] and then press [B] before releasing [A]. Then release [B]. would make sense.
This makes a lot of sense to me, I am keen to try to pick up some part of this as I mentioned yesterday but digging into the code, it is very much written with inputs in mind and it is a little unclear the best way to improve this... This looks like a great summary!
@kentcdodds Could you weigh in on this before we start any work here?
What do people expect
userEvent.typeto do? What should it do?
[...] I think most people expect it to be moreuserEvent press those buttons on the keyboard!.I think it should resolve around
document.getSelection()anduserEvent.type(null, "a{del}{shift}b{tab}c{/shift}[Semicolon]d{selectall}")should be a valid expression to test handling of hot keys and similar.
All userEvent methods should be: "fire all the same events that a user would if they were to perform that action." There's some subjectivity here. For example, in type, we fire some mouse and click events before typing, but some people might tab into the input rather than clicking on it. But in general we try to make the most sensible decision.
I guess it will be a step towards that premise:
userEvent.type(element, "foo", {skipClick = false}) for backward compatibility and as a shortcut which people are used to.userEvent.type(null, "foo", {skipClick = true}) to act on the activeElement right away.value | meaning
--- | ---
a | {key: 'a'}
{shift}a{/shift} |
{key: 'Shift'}{key: 'a', shiftKey: true}{key: 'Shift'}[ShiftLeft] | {code: 'ShiftLeft'}{[ShiftLeft]}[KeyA]{/[ShiftLeft]} | {code: 'ShiftLeft'}{code: 'KeyA', shiftKey: true}{code: 'ShiftLeft'}{a}[ShiftLeft]{/a} | {key: 'a'}{code: 'ShiftLeft'}{key: 'a'}key-code mapping for common US-QWERTY layout so that event handlers for both could exist at the same time.
- Keep
userEvent.type(element, "foo", {skipClick = false})for backward compatibility and as a shortcut which people are used to.- Prefer
userEvent.type(null, "foo", {skipClick = true})to act on theactiveElementright away.
It's unusual to have an optional argument at the beginning, and I think it adds more confusion than it's worth when it represents something different. Perhaps we could name it userEvent.typeActive or userEvent.typeAnywhere?
I tend to agree with @nickmccurdy. Would it make sense to think about the different types of thing a user is actually trying to do and approach them differently with a different API. Specifically
I feel like there is some core functionality in there perhaps but all the assumptions of clicking first and things like that, don't make sense for someone pressing say shift + f to do a shortcut key in your app.
As all Testing-Library packages aim for readability of the test, exposing the same core functionality per two APIs makes a lot of sense.
@sethreidnz What exactly do you have in mind regarding the assumptions apart from clicking first?
I think after clicking and setting the range the implementation already tries to follow the actual behavior in the browser regarding movement of focus etc and different behavior is considered a bug.
I've spotted another issue with userEvent.type and number fields. I think the approach suggested (fire key events at a specific element/the currently active element) will probably fix it. Any approach where type is too knowledgable about the element receiving the events is likely to be flaky.
If it's strictly necessary, I'd suggest an optional callback to allow the default 'type next key' behaviour to be overridden.
I have found what I believe to be a related issue to this one, which is documented in this CodeSandbox: https://codesandbox.io/s/userevent-unit-test-ncmgu?file=/src/App.test.js
Setup of the tested component
'1')HTMLInputElement.select() method on input focus so every new value completely overwrites the previous oneTest execution:
userEvent.type(... (ex: '11123')'23' and not '11123'@psullivan6 ~That seems to be a problem with jsdom. document.getSelection().rangeCount is 0 after element.select() in the test environment - but it is 1 in the browser.~ Nevermind, we're using element.selectionStart and element.selectionEnd in the implementation. Let's inspect it in the new implementation at #581
The rewrite is merged. The remaining issues can be tackled individually.