Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support empty string for maxlength #595

Merged
merged 4 commits into from
Mar 18, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
106 changes: 0 additions & 106 deletions src/__tests__/type.js
Original file line number Diff line number Diff line change
Expand Up @@ -255,112 +255,6 @@ test('should delay the typing when opts.delay is not 0', async () => {
}
})

test('honors maxlength', () => {
const {element, getEventSnapshot} = setup('<input maxlength="2" />')
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(
'<input value="12" maxlength="2" />',
)
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(
'<textarea maxlength="2">12</textarea>',
)

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(`<input maxlength="2" type="number" value="12" />`)

userEvent.type(element, '3')

expect(element).toHaveValue(123)
})

test('should fire events on the currently focused element', () => {
const {element} = setup(`<div><input /><input /></div>`, {
eventHandlers: {keyDown: handleKeyDown},
Expand Down
148 changes: 148 additions & 0 deletions src/__tests__/utils/edit/calculateNewValue.ts
Original file line number Diff line number Diff line change
@@ -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('<input maxlength="2" />')
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('<input maxlength="" />')
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(
'<input value="12" maxlength="2" />',
)
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(
'<textarea maxlength="2">12</textarea>',
)

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(`<input maxlength="2" type="number" value="12" />`)

userEvent.type(element as Element, '3')

expect(element).toHaveValue(123)
})
20 changes: 15 additions & 5 deletions src/utils/edit/calculateNewValue.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand Down Expand Up @@ -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,
Expand All @@ -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

Expand Down