From 8a34d0b7e9f7f96f5b88ef8d9927ffcc4717054d Mon Sep 17 00:00:00 2001 From: Ben Styles Date: Sat, 13 Feb 2021 12:44:09 +0100 Subject: [PATCH] fix: click links on 'enter' key press (#534) Trigger click event for userEvent.type('{enter}') on HTMLAnchorElements. Co-authored-by: Philipp Fritsche --- src/__tests__/type.js | 165 +++++++++++++++++++++++------------------- src/type.js | 14 +++- src/utils.js | 8 +- 3 files changed, 106 insertions(+), 81 deletions(-) diff --git a/src/__tests__/type.js b/src/__tests__/type.js index 18fb97d5..9940e78a 100644 --- a/src/__tests__/type.js +++ b/src/__tests__/type.js @@ -1121,43 +1121,43 @@ test('can type into an input with type `time`', () => { const {element, getEventSnapshot} = setup('') userEvent.type(element, '01:05') expect(getEventSnapshot()).toMatchInlineSnapshot(` - Events fired on: input[value="01:05"] - - input[value=""] - pointerover - input[value=""] - pointerenter - input[value=""] - mouseover: Left (0) - input[value=""] - mouseenter: Left (0) - input[value=""] - pointermove - input[value=""] - mousemove: Left (0) - input[value=""] - pointerdown - input[value=""] - mousedown: Left (0) - input[value=""] - focus - input[value=""] - focusin - input[value=""] - pointerup - input[value=""] - mouseup: Left (0) - input[value=""] - click: Left (0) - input[value=""] - keydown: 0 (48) - input[value=""] - keypress: 0 (48) - input[value=""] - keyup: 0 (48) - input[value=""] - keydown: 1 (49) - input[value=""] - keypress: 1 (49) - input[value=""] - keyup: 1 (49) - input[value=""] - keydown: : (58) - input[value=""] - keypress: : (58) - input[value=""] - keyup: : (58) - input[value=""] - keydown: 0 (48) - input[value=""] - keypress: 0 (48) - input[value="01:00"] - input - "{CURSOR}" -> "{CURSOR}01:00" - input[value="01:00"] - change - input[value="01:00"] - keyup: 0 (48) - input[value="01:00"] - keydown: 5 (53) - input[value="01:00"] - keypress: 5 (53) - input[value="01:05"] - input - "{CURSOR}01:00" -> "{CURSOR}01:05" - input[value="01:05"] - change - input[value="01:05"] - keyup: 5 (53) - `) + Events fired on: input[value="01:05"] + + input[value=""] - pointerover + input[value=""] - pointerenter + input[value=""] - mouseover: Left (0) + input[value=""] - mouseenter: Left (0) + input[value=""] - pointermove + input[value=""] - mousemove: Left (0) + input[value=""] - pointerdown + input[value=""] - mousedown: Left (0) + input[value=""] - focus + input[value=""] - focusin + input[value=""] - pointerup + input[value=""] - mouseup: Left (0) + input[value=""] - click: Left (0) + input[value=""] - keydown: 0 (48) + input[value=""] - keypress: 0 (48) + input[value=""] - keyup: 0 (48) + input[value=""] - keydown: 1 (49) + input[value=""] - keypress: 1 (49) + input[value=""] - keyup: 1 (49) + input[value=""] - keydown: : (58) + input[value=""] - keypress: : (58) + input[value=""] - keyup: : (58) + input[value=""] - keydown: 0 (48) + input[value=""] - keypress: 0 (48) + input[value="01:00"] - input + "{CURSOR}" -> "{CURSOR}01:00" + input[value="01:00"] - change + input[value="01:00"] - keyup: 0 (48) + input[value="01:00"] - keydown: 5 (53) + input[value="01:00"] - keypress: 5 (53) + input[value="01:05"] - input + "{CURSOR}01:00" -> "{CURSOR}01:05" + input[value="01:05"] - change + input[value="01:05"] - keyup: 5 (53) + `) expect(element).toHaveValue('01:05') }) @@ -1165,40 +1165,40 @@ test('can type into an input with type `time` without ":"', () => { const {element, getEventSnapshot} = setup('') userEvent.type(element, '0105') expect(getEventSnapshot()).toMatchInlineSnapshot(` - Events fired on: input[value="01:05"] - - input[value=""] - pointerover - input[value=""] - pointerenter - input[value=""] - mouseover: Left (0) - input[value=""] - mouseenter: Left (0) - input[value=""] - pointermove - input[value=""] - mousemove: Left (0) - input[value=""] - pointerdown - input[value=""] - mousedown: Left (0) - input[value=""] - focus - input[value=""] - focusin - input[value=""] - pointerup - input[value=""] - mouseup: Left (0) - input[value=""] - click: Left (0) - input[value=""] - keydown: 0 (48) - input[value=""] - keypress: 0 (48) - input[value=""] - keyup: 0 (48) - input[value=""] - keydown: 1 (49) - input[value=""] - keypress: 1 (49) - input[value=""] - keyup: 1 (49) - input[value=""] - keydown: 0 (48) - input[value=""] - keypress: 0 (48) - input[value="01:00"] - input - "{CURSOR}" -> "{CURSOR}01:00" - input[value="01:00"] - change - input[value="01:00"] - keyup: 0 (48) - input[value="01:00"] - keydown: 5 (53) - input[value="01:00"] - keypress: 5 (53) - input[value="01:05"] - input - "{CURSOR}01:00" -> "{CURSOR}01:05" - input[value="01:05"] - change - input[value="01:05"] - keyup: 5 (53) - `) + Events fired on: input[value="01:05"] + + input[value=""] - pointerover + input[value=""] - pointerenter + input[value=""] - mouseover: Left (0) + input[value=""] - mouseenter: Left (0) + input[value=""] - pointermove + input[value=""] - mousemove: Left (0) + input[value=""] - pointerdown + input[value=""] - mousedown: Left (0) + input[value=""] - focus + input[value=""] - focusin + input[value=""] - pointerup + input[value=""] - mouseup: Left (0) + input[value=""] - click: Left (0) + input[value=""] - keydown: 0 (48) + input[value=""] - keypress: 0 (48) + input[value=""] - keyup: 0 (48) + input[value=""] - keydown: 1 (49) + input[value=""] - keypress: 1 (49) + input[value=""] - keyup: 1 (49) + input[value=""] - keydown: 0 (48) + input[value=""] - keypress: 0 (48) + input[value="01:00"] - input + "{CURSOR}" -> "{CURSOR}01:00" + input[value="01:00"] - change + input[value="01:00"] - keyup: 0 (48) + input[value="01:00"] - keydown: 5 (53) + input[value="01:00"] - keypress: 5 (53) + input[value="01:05"] - input + "{CURSOR}01:00" -> "{CURSOR}01:05" + input[value="01:05"] - change + input[value="01:05"] - keyup: 5 (53) + `) expect(element).toHaveValue('01:05') }) @@ -1208,7 +1208,7 @@ test('can type more a number higher than 60 minutes into an input `time` and the expect(getEventSnapshot()).toMatchInlineSnapshot(` Events fired on: input[value="23:59"] - + input[value=""] - pointerover input[value=""] - pointerenter input[value=""] - mouseover: Left (0) @@ -1254,7 +1254,7 @@ test('can type letters into an input `time` and they are ignored', () => { expect(getEventSnapshot()).toMatchInlineSnapshot(` Events fired on: input[value="16:36"] - + input[value=""] - pointerover input[value=""] - pointerenter input[value=""] - mouseover: Left (0) @@ -1495,3 +1495,22 @@ test('{arrowup} fires keyup/keydown events', () => { input[value=""] - keyup: ArrowUp (38) `) }) + +test('{enter} fires click on links', () => { + const {element, getEventSnapshot} = setup('link') + + element?.focus() + + userEvent.type(element, '{enter}', {skipClick: true}) + + expect(getEventSnapshot()).toMatchInlineSnapshot(` + Events fired on: a + + a - focus + a - focusin + a - keydown: Enter (13) + a - keypress: Enter (13) + a - click: Left (0) + a - keyup: Enter (13) + `) +}) diff --git a/src/type.js b/src/type.js index 71fdf1cd..125c1968 100644 --- a/src/type.js +++ b/src/type.js @@ -8,13 +8,14 @@ import { getActiveElement, calculateNewValue, setSelectionRangeIfNecessary, - isClickable, + isClickableInput, isValidDateValue, getSelectionRange, getValue, isContentEditable, isValidInputTimeValue, buildTimeValue, + isInstanceOfElement, } from './utils' import {click} from './click' import {navigationKey} from './keys/navigation-key' @@ -321,7 +322,7 @@ function fireInputEventIfNeeded({ const prevValue = getValue(currentElement()) if ( !currentElement().readOnly && - !isClickable(currentElement()) && + !isClickableInput(currentElement()) && newValue !== prevValue ) { if (isContentEditable(currentElement())) { @@ -583,7 +584,12 @@ function handleEnter({currentElement, eventOverrides}) { }) if (keyPressDefaultNotPrevented) { - if (isClickable(currentElement())) { + if ( + isClickableInput(currentElement()) || + // Links with href defined should handle Enter the same as a click + (isInstanceOfElement(currentElement(), 'HTMLAnchorElement') && + currentElement().href) + ) { fireEvent.click(currentElement(), { ...eventOverrides, }) @@ -712,7 +718,7 @@ function handleSelectall({currentElement}) { } function handleSpace(context) { - if (isClickable(context.currentElement())) { + if (isClickableInput(context.currentElement())) { handleSpaceOnClickable(context) return } diff --git a/src/utils.js b/src/utils.js index 96578020..7b96a086 100644 --- a/src/utils.js +++ b/src/utils.js @@ -5,8 +5,8 @@ import {getWindowFromNode} from '@testing-library/dom/dist/helpers' /** * Check if an element is of a given type. * - * @param Element The element to test - * @param string Constructor name. E.g. 'HTMLSelectElement' + * @param {Element} element The element to test + * @param {string} elementType Constructor name. E.g. 'HTMLSelectElement' */ function isInstanceOfElement(element, elementType) { try { @@ -285,7 +285,7 @@ const CLICKABLE_INPUT_TYPES = [ 'submit', ] -function isClickable(element) { +function isClickableInput(element) { return ( element.tagName === 'BUTTON' || (isInstanceOfElement(element, 'HTMLInputElement') && @@ -353,7 +353,7 @@ function isValidInputTimeValue(element, timeValue) { export { FOCUSABLE_SELECTOR, isFocusable, - isClickable, + isClickableInput, getMouseEventOptions, isLabelWithInternallyDisabledControl, getActiveElement,