diff --git a/src/__tests__/type.js b/src/__tests__/type.js index 8b57efe5..316abf62 100644 --- a/src/__tests__/type.js +++ b/src/__tests__/type.js @@ -571,3 +571,55 @@ test('can type into an input with type `email`', async () => { await userEvent.type(element, email) expect(element).toHaveValue(email) }) + +// https://github.com/testing-library/user-event/issues/336 +test('can type "-" into number inputs', async () => { + const {element, getEventCalls} = setup('') + const negativeNumber = '-3' + await userEvent.type(element, negativeNumber) + expect(element).toHaveValue(-3) + + // NOTE: the input event here does not actually change the value thanks to + // weirdness with browsers. Then the second input event inserts both the + // - and the 3. /me rolls eyes + expect(getEventCalls()).toMatchInlineSnapshot(` + focus + keydown: - (45) + keypress: - (45) + input: "{CURSOR}" -> "" + keyup: - (45) + keydown: 3 (51) + keypress: 3 (51) + input: "{CURSOR}" -> "-3" + keyup: 3 (51) + `) +}) + +test('-{backspace}3', async () => { + const {element} = setup('') + const negativeNumber = '-{backspace}3' + await userEvent.type(element, negativeNumber) + expect(element).toHaveValue(3) +}) + +test('-a3', async () => { + const {element} = setup('') + const negativeNumber = '-a3' + await userEvent.type(element, negativeNumber) + expect(element).toHaveValue(-3) +}) + +test('typing an invalid input value', async () => { + const {element} = setup('') + await userEvent.type(element, '3-3') + + // TODO: fix this bug + // THIS IS A BUG! It should be expect(element.value).toBe('') + expect(element).toHaveValue(-3) + + // THIS IS A LIMITATION OF THE BROWSER + // It is impossible to programmatically set an input + // value to an invlid value. Not sure how to work around this + // but the badInput should actually be "true" if the user types "3-3" + expect(element.validity.badInput).toBe(false) +}) diff --git a/src/type.js b/src/type.js index 6f014cea..7c472b86 100644 --- a/src/type.js +++ b/src/type.js @@ -227,11 +227,13 @@ async function typeImpl( } } const eventOverrides = {} + let prevWasMinus for (const callback of eventCallbacks) { if (delay > 0) await wait(delay) if (!currentElement().disabled) { - const returnValue = await callback({eventOverrides}) + const returnValue = await callback({prevWasMinus, eventOverrides}) Object.assign(eventOverrides, returnValue?.eventOverrides) + prevWasMinus = returnValue?.prevWasMinus } } } @@ -241,7 +243,8 @@ async function typeImpl( newSelectionStart, eventOverrides, }) { - if (!currentElement().readOnly && newValue !== currentValue()) { + const prevValue = currentValue() + if (!currentElement().readOnly && newValue !== prevValue) { await tick() fireEvent.input(currentElement(), { @@ -251,6 +254,8 @@ async function typeImpl( setSelectionRange({newValue, newSelectionStart}) } + + return {prevValue} } // yes, calculateNewBackspaceValue and calculateNewValue look extremely similar @@ -334,9 +339,10 @@ async function typeImpl( } } - async function typeCharacter(char, {eventOverrides}) { + async function typeCharacter(char, {prevWasMinus = false, eventOverrides}) { const key = char // TODO: check if this also valid for characters with diacritic markers e.g. úé etc const keyCode = char.charCodeAt(0) + let nextPrevWasMinus const keyDownDefaultNotPrevented = fireEvent.keyDown(currentElement(), { key, @@ -356,10 +362,30 @@ async function typeImpl( }) if (keyPressDefaultNotPrevented) { - await fireInputEventIfNeeded({ - ...calculateNewValue(key), - eventOverrides, + const newEntry = prevWasMinus ? `-${char}` : char + + const {prevValue} = await fireInputEventIfNeeded({ + ...calculateNewValue(newEntry), + eventOverrides: { + data: key, + inputType: 'insertText', + ...eventOverrides, + }, }) + + // typing "-" into a number input will not actually update the value + // so for the next character we type, the value should be set to + // `-${newEntry}` + // we also preserve the prevWasMinus when the value is unchanged due + // to typing an invalid character (typing "-a3" results in "-3") + if (currentElement().type === 'number') { + const newValue = currentValue() + if (newValue === prevValue && newEntry !== '-') { + nextPrevWasMinus = prevWasMinus + } else { + nextPrevWasMinus = newEntry === '-' + } + } } } @@ -371,6 +397,8 @@ async function typeImpl( which: keyCode, ...eventOverrides, }) + + return {prevWasMinus: nextPrevWasMinus} } function modifier({name, key, keyCode, modifierProperty}) {