diff --git a/src/__tests__/type.js b/src/__tests__/type.js index 7a83b596..6d746bbe 100644 --- a/src/__tests__/type.js +++ b/src/__tests__/type.js @@ -255,112 +255,6 @@ test('should delay the typing when opts.delay is not 0', async () => { } }) -test('honors maxlength', () => { - const {element, getEventSnapshot} = setup('') - userEvent.type(element, '123') - - // NOTE: no input event when typing "3" - expect(getEventSnapshot()).toMatchInlineSnapshot(` - Events fired on: input[value="12"] - - 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: 1 (49) - input[value=""] - keypress: 1 (49) - input[value="1"] - input - "{CURSOR}" -> "1{CURSOR}" - input[value="1"] - keyup: 1 (49) - input[value="1"] - keydown: 2 (50) - input[value="1"] - keypress: 2 (50) - input[value="12"] - input - "1{CURSOR}" -> "12{CURSOR}" - input[value="12"] - keyup: 2 (50) - input[value="12"] - keydown: 3 (51) - input[value="12"] - keypress: 3 (51) - input[value="12"] - keyup: 3 (51) - `) -}) - -test('honors maxlength with existing text', () => { - const {element, getEventSnapshot} = setup( - '', - ) - userEvent.type(element, '3') - - // NOTE: no input event when typing "3" - expect(getEventSnapshot()).toMatchInlineSnapshot(` - Events fired on: input[value="12"] - - input[value="12"] - pointerover - input[value="12"] - pointerenter - input[value="12"] - mouseover: Left (0) - input[value="12"] - mouseenter: Left (0) - input[value="12"] - pointermove - input[value="12"] - mousemove: Left (0) - input[value="12"] - pointerdown - input[value="12"] - mousedown: Left (0) - input[value="12"] - focus - input[value="12"] - focusin - input[value="12"] - pointerup - input[value="12"] - mouseup: Left (0) - input[value="12"] - click: Left (0) - input[value="12"] - select - input[value="12"] - keydown: 3 (51) - input[value="12"] - keypress: 3 (51) - input[value="12"] - keyup: 3 (51) - `) -}) - -test('honors maxlength on textarea', () => { - const {element, getEventSnapshot} = setup( - '', - ) - - userEvent.type(element, '3') - - expect(getEventSnapshot()).toMatchInlineSnapshot(` - Events fired on: textarea[value="12"] - - textarea[value="12"] - pointerover - textarea[value="12"] - pointerenter - textarea[value="12"] - mouseover: Left (0) - textarea[value="12"] - mouseenter: Left (0) - textarea[value="12"] - pointermove - textarea[value="12"] - mousemove: Left (0) - textarea[value="12"] - pointerdown - textarea[value="12"] - mousedown: Left (0) - textarea[value="12"] - focus - textarea[value="12"] - focusin - textarea[value="12"] - pointerup - textarea[value="12"] - mouseup: Left (0) - textarea[value="12"] - click: Left (0) - textarea[value="12"] - select - textarea[value="12"] - keydown: 3 (51) - textarea[value="12"] - keypress: 3 (51) - textarea[value="12"] - keyup: 3 (51) - `) -}) - -// https://github.com/testing-library/user-event/issues/418 -test('ignores maxlength on input[type=number]', () => { - const {element} = setup(``) - - userEvent.type(element, '3') - - expect(element).toHaveValue(123) -}) - test('should fire events on the currently focused element', () => { const {element} = setup(`
`, { eventHandlers: {keyDown: handleKeyDown}, diff --git a/src/__tests__/utils/edit/calculateNewValue.ts b/src/__tests__/utils/edit/calculateNewValue.ts new file mode 100644 index 00000000..09b69648 --- /dev/null +++ b/src/__tests__/utils/edit/calculateNewValue.ts @@ -0,0 +1,148 @@ +import userEvent from 'index' +import {setup} from '__tests__/helpers/utils' + +// TODO: focus the maxlength tests on the tested aspects + +test('honors maxlength', () => { + const {element, getEventSnapshot} = setup('') + userEvent.type(element as Element, '123') + + // NOTE: no input event when typing "3" + expect(getEventSnapshot()).toMatchInlineSnapshot(` + Events fired on: input[value="12"] + + 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: 1 (49) + input[value=""] - keypress: 1 (49) + input[value="1"] - input + "{CURSOR}" -> "1{CURSOR}" + input[value="1"] - keyup: 1 (49) + input[value="1"] - keydown: 2 (50) + input[value="1"] - keypress: 2 (50) + input[value="12"] - input + "1{CURSOR}" -> "12{CURSOR}" + input[value="12"] - keyup: 2 (50) + input[value="12"] - keydown: 3 (51) + input[value="12"] - keypress: 3 (51) + input[value="12"] - keyup: 3 (51) + `) +}) + +test('honors maxlength="" as if there was no maxlength', () => { + const {element, getEventSnapshot} = setup('') + userEvent.type(element as Element, '123') + + expect(getEventSnapshot()).toMatchInlineSnapshot(` + Events fired on: input[value="123"] + + 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: 1 (49) + input[value=""] - keypress: 1 (49) + input[value="1"] - input + "{CURSOR}" -> "1{CURSOR}" + input[value="1"] - keyup: 1 (49) + input[value="1"] - keydown: 2 (50) + input[value="1"] - keypress: 2 (50) + input[value="12"] - input + "1{CURSOR}" -> "12{CURSOR}" + input[value="12"] - keyup: 2 (50) + input[value="12"] - keydown: 3 (51) + input[value="12"] - keypress: 3 (51) + input[value="123"] - input + "12{CURSOR}" -> "123{CURSOR}" + input[value="123"] - keyup: 3 (51) + `) +}) + +test('honors maxlength with existing text', () => { + const {element, getEventSnapshot} = setup( + '', + ) + userEvent.type(element as Element, '3') + + // NOTE: no input event when typing "3" + expect(getEventSnapshot()).toMatchInlineSnapshot(` + Events fired on: input[value="12"] + + input[value="12"] - pointerover + input[value="12"] - pointerenter + input[value="12"] - mouseover: Left (0) + input[value="12"] - mouseenter: Left (0) + input[value="12"] - pointermove + input[value="12"] - mousemove: Left (0) + input[value="12"] - pointerdown + input[value="12"] - mousedown: Left (0) + input[value="12"] - focus + input[value="12"] - focusin + input[value="12"] - pointerup + input[value="12"] - mouseup: Left (0) + input[value="12"] - click: Left (0) + input[value="12"] - select + input[value="12"] - keydown: 3 (51) + input[value="12"] - keypress: 3 (51) + input[value="12"] - keyup: 3 (51) + `) +}) + +test('honors maxlength on textarea', () => { + const {element, getEventSnapshot} = setup( + '', + ) + + userEvent.type(element as Element, '3') + + expect(getEventSnapshot()).toMatchInlineSnapshot(` + Events fired on: textarea[value="12"] + + textarea[value="12"] - pointerover + textarea[value="12"] - pointerenter + textarea[value="12"] - mouseover: Left (0) + textarea[value="12"] - mouseenter: Left (0) + textarea[value="12"] - pointermove + textarea[value="12"] - mousemove: Left (0) + textarea[value="12"] - pointerdown + textarea[value="12"] - mousedown: Left (0) + textarea[value="12"] - focus + textarea[value="12"] - focusin + textarea[value="12"] - pointerup + textarea[value="12"] - mouseup: Left (0) + textarea[value="12"] - click: Left (0) + textarea[value="12"] - select + textarea[value="12"] - keydown: 3 (51) + textarea[value="12"] - keypress: 3 (51) + textarea[value="12"] - keyup: 3 (51) + `) +}) + +// https://github.com/testing-library/user-event/issues/418 +test('ignores maxlength on input[type=number]', () => { + const {element} = setup(``) + + userEvent.type(element as Element, '3') + + expect(element).toHaveValue(123) +}) diff --git a/src/utils/edit/calculateNewValue.ts b/src/utils/edit/calculateNewValue.ts index 03e929be..5ec09bf2 100644 --- a/src/utils/edit/calculateNewValue.ts +++ b/src/utils/edit/calculateNewValue.ts @@ -14,10 +14,6 @@ export function calculateNewValue( } { const {selectionStart, selectionEnd} = selectionRange - // can't use .maxLength property because of a jsdom bug: - // https://github.com/jsdom/jsdom/issues/2927 - const maxLength = Number(element.getAttribute('maxlength') ?? -1) - let newValue: string, newSelectionStart: number if (selectionStart === null) { @@ -72,7 +68,11 @@ export function calculateNewValue( } } - if (!supportsMaxLength(element) || maxLength < 0) { + // can't use .maxLength property because of a jsdom bug: + // https://github.com/jsdom/jsdom/issues/2927 + const maxLength = getSanitizedMaxLength(element) + + if (maxLength === undefined) { return { newValue, newSelectionStart, @@ -86,6 +86,16 @@ export function calculateNewValue( } } +function getSanitizedMaxLength(element: Element) { + if (!supportsMaxLength(element)) { + return undefined + } + + const attr = element.getAttribute('maxlength') ?? '' + + return /^\d+$/.test(attr) && Number(attr) >= 0 ? Number(attr) : undefined +} + function supportsMaxLength(element: Element) { if (element.tagName === 'TEXTAREA') return true