Skip to content

Commit 85546ec

Browse files
9stillGreg Shtilman
and
Greg Shtilman
authored
fix(type): maxlength handling over multiple type() calls (testing-library#283)
Co-authored-by: Greg Shtilman <[email protected]>
1 parent b470e26 commit 85546ec

File tree

3 files changed

+109
-5
lines changed

3 files changed

+109
-5
lines changed

__tests__/react/type.js

+55-1
Original file line numberDiff line numberDiff line change
@@ -159,7 +159,7 @@ describe("userEvent.type", () => {
159159
);
160160

161161
it.each(["input", "textarea"])(
162-
"should type text in <%s> up to maxLength if provided",
162+
"should enter text in <%s> up to maxLength if provided",
163163
(type) => {
164164
const onChange = jest.fn();
165165
const onKeyDown = jest.fn();
@@ -208,4 +208,58 @@ describe("userEvent.type", () => {
208208
expect(onKeyUp).not.toHaveBeenCalled();
209209
}
210210
);
211+
212+
it.each(["input", "textarea"])(
213+
"should append text in <%s> up to maxLength if provided",
214+
(type) => {
215+
const onChange = jest.fn();
216+
const onKeyDown = jest.fn();
217+
const onKeyPress = jest.fn();
218+
const onKeyUp = jest.fn();
219+
const maxLength = 10;
220+
221+
const { getByTestId } = render(
222+
React.createElement(type, {
223+
"data-testid": "input",
224+
onChange,
225+
onKeyDown,
226+
onKeyPress,
227+
onKeyUp,
228+
maxLength,
229+
})
230+
);
231+
232+
const text1 = "superlong";
233+
const text2 = "text";
234+
const text = text1 + text2;
235+
const slicedText = text.slice(0, maxLength);
236+
237+
const inputEl = getByTestId("input");
238+
239+
userEvent.type(inputEl, text1);
240+
userEvent.type(inputEl, text2);
241+
242+
expect(inputEl).toHaveProperty("value", slicedText);
243+
expect(onChange).toHaveBeenCalledTimes(slicedText.length);
244+
expect(onKeyPress).toHaveBeenCalledTimes(text.length);
245+
expect(onKeyDown).toHaveBeenCalledTimes(text.length);
246+
expect(onKeyUp).toHaveBeenCalledTimes(text.length);
247+
248+
inputEl.value = "";
249+
onChange.mockClear();
250+
onKeyPress.mockClear();
251+
onKeyDown.mockClear();
252+
onKeyUp.mockClear();
253+
254+
userEvent.type(inputEl, text, {
255+
allAtOnce: true,
256+
});
257+
258+
expect(inputEl).toHaveProperty("value", slicedText);
259+
expect(onChange).toHaveBeenCalledTimes(1);
260+
expect(onKeyPress).not.toHaveBeenCalled();
261+
expect(onKeyDown).not.toHaveBeenCalled();
262+
expect(onKeyUp).not.toHaveBeenCalled();
263+
}
264+
);
211265
});

__tests__/vue/type.js

+49-1
Original file line numberDiff line numberDiff line change
@@ -147,7 +147,7 @@ describe("userEvent.type", () => {
147147
);
148148

149149
it.each(["input", "textarea"])(
150-
"should type text in <%s> up to maxLength if provided",
150+
"should enter text in <%s> up to maxLength if provided",
151151
(type) => {
152152
const input = jest.fn();
153153
const keydown = jest.fn();
@@ -189,4 +189,52 @@ describe("userEvent.type", () => {
189189
expect(keyup).not.toHaveBeenCalled();
190190
}
191191
);
192+
193+
it.each(["input", "textarea"])(
194+
"should append text in <%s> up to maxLength if provided",
195+
(type) => {
196+
const input = jest.fn();
197+
const keydown = jest.fn();
198+
const keypress = jest.fn();
199+
const keyup = jest.fn();
200+
const maxLength = 10;
201+
202+
const { getByTestId } = renderComponent(
203+
type,
204+
{ input, keydown, keypress, keyup },
205+
{ maxLength }
206+
);
207+
208+
const text1 = "superlong";
209+
const text2 = "text";
210+
const text = text1 + text2;
211+
const slicedText = text.slice(0, maxLength);
212+
213+
const inputEl = getByTestId("input");
214+
215+
userEvent.type(inputEl, text1);
216+
userEvent.type(inputEl, text2);
217+
218+
expect(inputEl).toHaveProperty("value", slicedText);
219+
expect(keydown).toHaveBeenCalledTimes(text.length);
220+
expect(keypress).toHaveBeenCalledTimes(text.length);
221+
expect(keyup).toHaveBeenCalledTimes(text.length);
222+
223+
inputEl.value = "";
224+
input.mockClear();
225+
keydown.mockClear();
226+
keypress.mockClear();
227+
keyup.mockClear();
228+
229+
userEvent.type(inputEl, text, {
230+
allAtOnce: true,
231+
});
232+
233+
expect(inputEl).toHaveProperty("value", slicedText);
234+
expect(input).toHaveBeenCalledTimes(1);
235+
expect(keydown).not.toHaveBeenCalled();
236+
expect(keypress).not.toHaveBeenCalled();
237+
expect(keyup).not.toHaveBeenCalled();
238+
}
239+
);
192240
});

src/index.js

+5-3
Original file line numberDiff line numberDiff line change
@@ -236,11 +236,13 @@ const userEvent = {
236236
};
237237
const opts = Object.assign(defaultOpts, userOpts);
238238

239-
const computedText =
240-
element.maxLength > 0 ? text.slice(0, element.maxLength) : text;
241-
242239
const previousText = element.value;
243240

241+
const computedText =
242+
element.maxLength > 0
243+
? text.slice(0, Math.max(element.maxLength - previousText.length, 0))
244+
: text;
245+
244246
if (opts.allAtOnce) {
245247
if (element.readOnly) return;
246248
fireEvent.input(element, {

0 commit comments

Comments
 (0)