import { fireEvent } from '@testing-library/dom' ; import { Config } from '../setup' ; import { getActiveElement , getKeyEventProps , wait } from '../utils' ; import { behaviorPlugin , keyboardKey } from './types' ; import * as plugins from './plugins' ; export interface KeyboardAction { keyDef : keyboardKey ; releasePrevious : boolean ; releaseSelf : boolean ; repeat : number ; } export async function keyboardAction ( config , actions ) { for ( let i = 0 ; i < actions . length ; i ++ ) { await keyboardKeyAction ( config , actions [ i ] ) ; if ( typeof config . delay === 'number' && i < actions . length - 1 ) { await wait ( config . delay ) ; } } } async function keyboardKeyAction ( config , { keyDef , releasePrevious , releaseSelf , repeat } ) { const { document , keyboardState , delay } = config ; const getCurrentElement = ( ) => getActive ( document ) ; const pressed = keyboardState . pressed . find ( p => p . keyDef === keyDef ) ; if ( pressed ) { await keyup ( keyDef , getCurrentElement , config , pressed . unpreventedDefault ) ; } if ( ! releasePrevious ) { let unpreventedDefault = true ; for ( let i = 1 ; i <= repeat ; i ++ ) { unpreventedDefault = await keydown ( keyDef , getCurrentElement , config ) ; if ( unpreventedDefault && hasKeyPress ( keyDef , config ) ) { await keypress ( keyDef , getCurrentElement , config ) ; } if ( typeof delay === 'number' && i < repeat ) { await wait ( delay ) ; } } if ( releaseSelf ) { await keyup ( keyDef , getCurrentElement , config , unpreventedDefault ) ; } } } function getActive ( document ) { return getActiveElement ( document ) ?? document . body ; } export async function releaseAllKeys ( config ) { const getCurrentElement = ( ) => getActive ( config . document ) ; for ( const k of config . keyboardState . pressed ) { await keyup ( k . keyDef , getCurrentElement , config , k . unpreventedDefault ) ; } } async function keydown ( keyDef , getCurrentElement , config ) { const element = getCurrentElement ( ) ; if ( element !== config . keyboardState . activeElement ) { config . keyboardState . carryValue = undefined ; config . keyboardState . carryChar = '' ; } config . keyboardState . activeElement = element ; applyPlugins ( plugins . preKeydownBehavior , keyDef , element , config ) ; const unpreventedDefault = fireEvent . keyDown ( element , getKeyEventProps ( keyDef , config . keyboardState ) ) ; config . keyboardState . pressed . push ( { keyDef , unpreventedDefault } ) ; if ( unpreventedDefault ) { applyPlugins ( plugins . keydownBehavior , keyDef , getCurrentElement ( ) , config ) ; } return unpreventedDefault ; } async function keypress ( keyDef , getCurrentElement , config ) { const element = getCurrentElement ( ) ; const unpreventedDefault = fireEvent . keyPress ( element , { ... getKeyEventProps ( keyDef , config . keyboardState ) , charCode : keyDef . key === 'Enter' ? 13 : String ( keyDef . key ) . charCodeAt ( 0 ) , } ) ; if ( unpreventedDefault ) { applyPlugins ( plugins . keypressBehavior , keyDef , getCurrentElement ( ) , config ) ; } } async function keyup ( keyDef , getCurrentElement , config , unprevented ) { const element = getCurrentElement ( ) ; applyPlugins ( plugins . preKeyupBehavior , keyDef , element , config ) ; const unpreventedDefault = fireEvent . keyUp ( element , getKeyEventProps ( keyDef , config . keyboardState ) ) ; if ( unprevented && unpreventedDefault ) { applyPlugins ( plugins . keyupBehavior , keyDef , getCurrentElement ( ) , config ) ; } config . keyboardState . pressed = config . keyboardState . pressed . filter ( k => k . keyDef !== keyDef ) ; applyPlugins ( plugins . postKeyupBehavior , keyDef , element , config ) ; } function applyPlugins ( pluginCollection , keyDef , element , config ) { const plugin = pluginCollection . find ( p => p . matches ( keyDef , element , config ) ) ; if ( plugin ) { plugin . handle ( keyDef , element , config ) ; } return ! ! plugin ; } function hasKeyPress ( keyDef , config ) { return ( ( keyDef . key ?. length === 1 || keyDef . key === 'Enter' ) && ! config . keyboardState . modifiers . Control && ! config . keyboardState . modifiers . Alt ) ; }