@@ -208,4 +208,353 @@ describe("userEvent.type", () => {
208208 expect ( onKeyUp ) . not . toHaveBeenCalled ( ) ;
209209 }
210210 ) ;
211+
212+ describe ( "special characters" , ( ) => {
213+ afterEach ( jest . clearAllMocks ) ;
214+
215+ const onChange = jest . fn ( ) . mockImplementation ( e => e . persist ( ) ) ;
216+ const onKeyDown = jest . fn ( ) . mockImplementation ( e => e . persist ( ) ) ;
217+ const onKeyPress = jest . fn ( ) . mockImplementation ( e => e . persist ( ) ) ;
218+ const onKeyUp = jest . fn ( ) . mockImplementation ( e => e . persist ( ) ) ;
219+
220+ it . each ( [ "a{bc" , "a{bc}" , "a{backspacee}c" ] ) (
221+ "properly parses %s" ,
222+ async text => {
223+ const { getByTestId } = render (
224+ React . createElement ( "input" , {
225+ "data-testid" : "input"
226+ } )
227+ ) ;
228+
229+ const inputEl = getByTestId ( "input" ) ;
230+
231+ await userEvent . type ( inputEl , text ) ;
232+
233+ expect ( inputEl ) . toHaveProperty ( "value" , text ) ;
234+ }
235+ ) ;
236+
237+ describe ( "{enter}" , ( ) => {
238+ describe ( "input" , ( ) => {
239+ it ( "should record key up/down/press events from {enter}" , async ( ) => {
240+ const { getByTestId } = render (
241+ React . createElement ( "input" , {
242+ "data-testid" : "input" ,
243+ onChange,
244+ onKeyDown,
245+ onKeyPress,
246+ onKeyUp
247+ } )
248+ ) ;
249+
250+ const text = "abc{enter}" ;
251+
252+ const inputEl = getByTestId ( "input" ) ;
253+
254+ await userEvent . type ( inputEl , text ) ;
255+
256+ const expectedText = "abc" ;
257+
258+ expect ( inputEl ) . toHaveProperty ( "value" , expectedText ) ;
259+ expect ( onChange ) . toHaveBeenCalledTimes ( 3 ) ;
260+ expect ( onKeyPress ) . toHaveBeenCalledTimes ( 4 ) ;
261+ expect ( onKeyDown ) . toHaveBeenCalledTimes ( 4 ) ;
262+ expect ( onKeyUp ) . toHaveBeenCalledTimes ( 4 ) ;
263+ } ) ;
264+ } ) ;
265+
266+ describe ( "textarea" , ( ) => {
267+ it ( "should be able to type newlines with {enter}" , async ( ) => {
268+ const { getByTestId } = render (
269+ React . createElement ( "textarea" , {
270+ "data-testid" : "input" ,
271+ onChange,
272+ onKeyDown,
273+ onKeyPress,
274+ onKeyUp
275+ } )
276+ ) ;
277+
278+ const text = "a{enter}{enter}b{enter}" ;
279+
280+ const inputEl = getByTestId ( "input" ) ;
281+
282+ await userEvent . type ( inputEl , text ) ;
283+
284+ const expectedText = "a\n\nb\n" ;
285+
286+ expect ( inputEl ) . toHaveProperty ( "value" , expectedText ) ;
287+ expect ( onChange ) . toHaveBeenCalledTimes ( 5 ) ;
288+ expect ( onKeyPress ) . toHaveBeenCalledTimes ( 5 ) ;
289+ expect ( onKeyDown ) . toHaveBeenCalledTimes ( 5 ) ;
290+ expect ( onKeyUp ) . toHaveBeenCalledTimes ( 5 ) ;
291+ } ) ;
292+ } ) ;
293+ } ) ;
294+
295+ describe ( "{esc}" , ( ) => {
296+ describe ( "input" , ( ) => {
297+ it ( "should record key up/down/press events from {esc}" , async ( ) => {
298+ const { getByTestId } = render (
299+ React . createElement ( "input" , {
300+ "data-testid" : "input" ,
301+ onChange,
302+ onKeyDown,
303+ onKeyPress,
304+ onKeyUp
305+ } )
306+ ) ;
307+
308+ const text = "a{esc}" ;
309+
310+ const inputEl = getByTestId ( "input" ) ;
311+
312+ await userEvent . type ( inputEl , text ) ;
313+
314+ const expectedText = "a" ;
315+
316+ expect ( inputEl ) . toHaveProperty ( "value" , expectedText ) ;
317+ expect ( onChange ) . toHaveBeenCalledTimes ( 1 ) ;
318+ expect ( onKeyPress ) . toHaveBeenCalledTimes ( 1 ) ;
319+ expect ( onKeyDown ) . toHaveBeenCalledTimes ( 2 ) ;
320+ expect ( onKeyUp ) . toHaveBeenCalledTimes ( 2 ) ;
321+ } ) ;
322+ } ) ;
323+
324+ describe ( "textarea" , ( ) => {
325+ it ( "should be able to type newlines with {esc}" , async ( ) => {
326+ const { getByTestId } = render (
327+ React . createElement ( "textarea" , {
328+ "data-testid" : "input" ,
329+ onChange,
330+ onKeyDown,
331+ onKeyPress,
332+ onKeyUp
333+ } )
334+ ) ;
335+
336+ const text = "a{esc}" ;
337+
338+ const inputEl = getByTestId ( "input" ) ;
339+
340+ await userEvent . type ( inputEl , text ) ;
341+
342+ const expectedText = "a" ;
343+
344+ expect ( inputEl ) . toHaveProperty ( "value" , expectedText ) ;
345+ expect ( onChange ) . toHaveBeenCalledTimes ( 1 ) ;
346+ expect ( onKeyPress ) . toHaveBeenCalledTimes ( 1 ) ;
347+ expect ( onKeyDown ) . toHaveBeenCalledTimes ( 2 ) ;
348+ expect ( onKeyUp ) . toHaveBeenCalledTimes ( 2 ) ;
349+ } ) ;
350+ } ) ;
351+ } ) ;
352+
353+ describe ( "{backspace}" , ( ) => {
354+ describe . each ( [ "input" , "textarea" ] ) ( "%s" , elementType => {
355+ it . each ( [
356+ [
357+ "ab{backspace}c" ,
358+ "ac" ,
359+ { keyDown : 4 , keyUp : 4 , keyPress : 3 , change : 4 }
360+ ] ,
361+ [
362+ "a{backspace}{backspace}bc" ,
363+ "bc" ,
364+ { keyDown : 5 , keyUp : 5 , keyPress : 3 , change : 4 }
365+ ] ,
366+ [
367+ "a{{backspace}}" ,
368+ "a}" ,
369+ { keyDown : 4 , keyUp : 4 , keyPress : 3 , change : 4 }
370+ ]
371+ ] ) (
372+ "input `%s` should output `%s` and have the correct number of fired events" ,
373+ async (
374+ typeText ,
375+ expectedText ,
376+ {
377+ keyDown : numKeyDownEvents ,
378+ keyUp : numKeyUpEvents ,
379+ keyPress : numKeyPressEvents ,
380+ change : numOnChangeEvents
381+ }
382+ ) => {
383+ const { getByTestId } = render (
384+ React . createElement ( elementType , {
385+ "data-testid" : "input" ,
386+ onChange,
387+ onKeyDown,
388+ onKeyPress,
389+ onKeyUp
390+ } )
391+ ) ;
392+
393+ const inputEl = getByTestId ( "input" ) ;
394+
395+ await userEvent . type ( inputEl , typeText ) ;
396+
397+ expect ( inputEl ) . toHaveProperty ( "value" , expectedText ) ;
398+ expect ( onChange ) . toHaveBeenCalledTimes ( numOnChangeEvents ) ;
399+ expect ( onKeyDown ) . toHaveBeenCalledTimes ( numKeyDownEvents ) ;
400+ expect ( onKeyUp ) . toHaveBeenCalledTimes ( numKeyUpEvents ) ;
401+ expect ( onKeyPress ) . toHaveBeenCalledTimes ( numKeyPressEvents ) ;
402+ }
403+ ) ;
404+ } ) ;
405+ } ) ;
406+
407+ describe ( "modifiers" , ( ) => {
408+ describe . each ( [
409+ [ "shift" , "Shift" , "shiftKey" ] ,
410+ [ "ctrl" , "Control" , "ctrlKey" ] ,
411+ [ "alt" , "Alt" , "altKey" ] ,
412+ [ "meta" , "OS" , "metaKey" ]
413+ ] ) ( "%s" , ( modifierText , modifierKey , modifierProperty ) => {
414+ describe . each ( [ "input" , "textarea" ] ) ( "%s" , elementType => {
415+ it ( "only adds modifier to following keystroke" , async ( ) => {
416+ const handler = jest . fn ( ) . mockImplementation ( e => e . persist ( ) ) ;
417+
418+ const { getByTestId } = render (
419+ React . createElement ( elementType , {
420+ "data-testid" : "input" ,
421+ onKeyDown : handler ,
422+ onKeyPress : handler ,
423+ onKeyUp : handler
424+ } )
425+ ) ;
426+
427+ const inputEl = getByTestId ( "input" ) ;
428+
429+ await userEvent . type ( inputEl , `{${ modifierText } }ab` ) ;
430+
431+ expect ( inputEl ) . toHaveProperty ( "value" , "ab" ) ;
432+
433+ expect ( handler ) . toHaveBeenCalledWithEventAtIndex ( 0 , {
434+ type : "keydown" ,
435+ key : modifierKey ,
436+ [ modifierProperty ] : false
437+ } ) ;
438+ expect ( handler ) . toHaveBeenCalledWithEventAtIndex ( 1 , {
439+ type : "keydown" ,
440+ key : "a" ,
441+ [ modifierProperty ] : true
442+ } ) ;
443+ expect ( handler ) . toHaveBeenCalledWithEventAtIndex ( 2 , {
444+ type : "keypress" ,
445+ key : "a" ,
446+ [ modifierProperty ] : true
447+ } ) ;
448+ expect ( handler ) . toHaveBeenCalledWithEventAtIndex ( 3 , {
449+ type : "keyup" ,
450+ key : "a" ,
451+ [ modifierProperty ] : true
452+ } ) ;
453+ expect ( handler ) . toHaveBeenCalledWithEventAtIndex ( 4 , {
454+ type : "keyup" ,
455+ key : modifierKey ,
456+ [ modifierProperty ] : false
457+ } ) ;
458+ expect ( handler ) . toHaveBeenCalledWithEventAtIndex ( 5 , {
459+ type : "keydown" ,
460+ key : "b" ,
461+ [ modifierProperty ] : false
462+ } ) ;
463+ expect ( handler ) . toHaveBeenCalledWithEventAtIndex ( 6 , {
464+ type : "keypress" ,
465+ key : "b" ,
466+ [ modifierProperty ] : false
467+ } ) ;
468+ expect ( handler ) . toHaveBeenCalledWithEventAtIndex ( 7 , {
469+ type : "keyup" ,
470+ key : "b" ,
471+ [ modifierProperty ] : false
472+ } ) ;
473+ } ) ;
474+ } ) ;
475+ } ) ;
476+
477+ it ( "can handle multiple held modifiers" , async ( ) => {
478+ const handler = jest . fn ( ) . mockImplementation ( e => e . persist ( ) ) ;
479+
480+ const { getByTestId } = render (
481+ React . createElement ( "input" , {
482+ "data-testid" : "input" ,
483+ onKeyDown : handler ,
484+ onKeyPress : handler ,
485+ onKeyUp : handler
486+ } )
487+ ) ;
488+
489+ const inputEl = getByTestId ( "input" ) ;
490+
491+ await userEvent . type ( inputEl , "{ctrl}{shift}ab" ) ;
492+
493+ expect ( inputEl ) . toHaveProperty ( "value" , "ab" ) ;
494+
495+ expect ( handler ) . toHaveBeenCalledTimes ( 10 ) ;
496+
497+ expect ( handler ) . toHaveBeenCalledWithEventAtIndex ( 0 , {
498+ type : "keydown" ,
499+ key : "Control" ,
500+ ctrlKey : false ,
501+ shiftKey : false
502+ } ) ;
503+ expect ( handler ) . toHaveBeenCalledWithEventAtIndex ( 1 , {
504+ type : "keydown" ,
505+ key : "Shift" ,
506+ ctrlKey : true ,
507+ shiftKey : false
508+ } ) ;
509+ expect ( handler ) . toHaveBeenCalledWithEventAtIndex ( 2 , {
510+ type : "keydown" ,
511+ key : "a" ,
512+ ctrlKey : true ,
513+ shiftKey : true
514+ } ) ;
515+ expect ( handler ) . toHaveBeenCalledWithEventAtIndex ( 3 , {
516+ type : "keypress" ,
517+ key : "a" ,
518+ ctrlKey : true ,
519+ shiftKey : true
520+ } ) ;
521+ expect ( handler ) . toHaveBeenCalledWithEventAtIndex ( 4 , {
522+ type : "keyup" ,
523+ key : "a" ,
524+ ctrlKey : true ,
525+ shiftKey : true
526+ } ) ;
527+ expect ( handler ) . toHaveBeenCalledWithEventAtIndex ( 5 , {
528+ type : "keyup" ,
529+ key : "Control" ,
530+ ctrlKey : false ,
531+ shiftKey : true
532+ } ) ;
533+ expect ( handler ) . toHaveBeenCalledWithEventAtIndex ( 6 , {
534+ type : "keyup" ,
535+ key : "Shift" ,
536+ ctrlKey : false ,
537+ shiftKey : false
538+ } ) ;
539+ expect ( handler ) . toHaveBeenCalledWithEventAtIndex ( 7 , {
540+ type : "keydown" ,
541+ key : "b" ,
542+ ctrlKey : false ,
543+ shiftKey : false
544+ } ) ;
545+ expect ( handler ) . toHaveBeenCalledWithEventAtIndex ( 8 , {
546+ type : "keypress" ,
547+ key : "b" ,
548+ ctrlKey : false ,
549+ shiftKey : false
550+ } ) ;
551+ expect ( handler ) . toHaveBeenCalledWithEventAtIndex ( 9 , {
552+ type : "keyup" ,
553+ key : "b" ,
554+ ctrlKey : false ,
555+ shiftKey : false
556+ } ) ;
557+ } ) ;
558+ } ) ;
559+ } ) ;
211560} ) ;
0 commit comments