Skip to content

Commit ced7046

Browse files
fix: type should not type in readonly or disabled fields (#51)
1 parent ce4416f commit ced7046

File tree

2 files changed

+94
-27
lines changed

2 files changed

+94
-27
lines changed

projects/testing-library/src/lib/user-events/type.ts

+23-14
Original file line numberDiff line numberDiff line change
@@ -38,11 +38,18 @@ export function createType(fireEvent: FireFunction & FireObject) {
3838

3939
return async function type(element: HTMLElement, value: string | number, options?: TypeOptions) {
4040
const { allAtOnce = false, delay = 0 } = options || {};
41-
const initialValue = (element as HTMLInputElement).value;
41+
const inputElement = element as HTMLInputElement;
42+
const initialValue = inputElement.value;
43+
44+
if (inputElement.disabled) {
45+
return;
46+
}
4247

4348
if (allAtOnce || value === '') {
44-
fireEvent.input(element, { target: { value } });
45-
element.addEventListener('blur', createFireChangeEvent(initialValue));
49+
if (!inputElement.readOnly) {
50+
fireEvent.input(inputElement, { target: { value } });
51+
}
52+
inputElement.addEventListener('blur', createFireChangeEvent(initialValue));
4653
return;
4754
}
4855

@@ -57,38 +64,40 @@ export function createType(fireEvent: FireFunction & FireObject) {
5764
await wait(delay);
5865
}
5966

60-
const downEvent = fireEvent.keyDown(element, {
67+
const downEvent = fireEvent.keyDown(inputElement, {
6168
key: key,
6269
keyCode: keyCode,
6370
which: keyCode,
6471
});
6572

6673
if (downEvent) {
67-
const pressEvent = fireEvent.keyPress(element, {
74+
const pressEvent = fireEvent.keyPress(inputElement, {
6875
key: key,
6976
keyCode,
7077
charCode: keyCode,
7178
});
7279

7380
if (pressEvent) {
7481
actuallyTyped += key;
75-
fireEvent.input(element, {
76-
target: {
77-
value: actuallyTyped,
78-
},
79-
bubbles: true,
80-
cancelable: true,
81-
});
82+
if (!inputElement.readOnly) {
83+
fireEvent.input(inputElement, {
84+
target: {
85+
value: actuallyTyped,
86+
},
87+
bubbles: true,
88+
cancelable: true,
89+
});
90+
}
8291
}
8392
}
8493

85-
fireEvent.keyUp(element, {
94+
fireEvent.keyUp(inputElement, {
8695
key: key,
8796
keyCode: keyCode,
8897
which: keyCode,
8998
});
9099
}
91100

92-
element.addEventListener('blur', createFireChangeEvent(initialValue));
101+
inputElement.addEventListener('blur', createFireChangeEvent(initialValue));
93102
};
94103
}

projects/testing-library/tests/user-events/type.spec.ts

+71-13
Original file line numberDiff line numberDiff line change
@@ -178,13 +178,15 @@ describe('options', () => {
178178
});
179179
});
180180

181-
test('does not type when event.preventDefault() is called', async () => {
181+
describe('does not type when ', () => {
182182
@Component({
183183
selector: 'fixture',
184184
template: `
185185
<input
186186
type="text"
187187
data-testid="input"
188+
[disabled]="disabled"
189+
[readonly]="readonly"
188190
(input)="onInput($event)"
189191
(change)="onChange($event)"
190192
(keydown)="onKeyDown($event)"
@@ -194,30 +196,86 @@ test('does not type when event.preventDefault() is called', async () => {
194196
`,
195197
})
196198
class FixtureComponent {
199+
@Input() disabled = false;
200+
@Input() readonly = false;
201+
197202
onInput($event) {}
198203
onChange($event) {}
199204
onKeyDown($event) {}
200205
onKeyPress($event) {}
201206
onKeyUp($event) {}
202207
}
203208

204-
const componentProperties = {
205-
onChange: jest.fn(),
206-
onKeyDown: jest.fn().mockImplementation(event => event.preventDefault()),
207-
};
209+
test('input is disabled', async () => {
210+
const componentEvents = {
211+
onInput: jest.fn(),
212+
onChange: jest.fn(),
213+
onKeyDown: jest.fn(),
214+
onKeyPress: jest.fn(),
215+
onKeyUp: jest.fn(),
216+
};
217+
218+
const component = await render(FixtureComponent, {
219+
componentProperties: {
220+
disabled: true,
221+
...componentEvents,
222+
},
223+
});
224+
225+
const inputControl = component.getByTestId('input') as HTMLInputElement;
226+
component.type(inputControl, 'Hello');
208227

209-
const component = await render(FixtureComponent, { componentProperties });
228+
Object.values(componentEvents).forEach(evt => expect(evt).not.toHaveBeenCalled());
229+
expect(inputControl.value).toBe('');
230+
});
210231

211-
const inputControl = component.getByTestId('input') as HTMLInputElement;
212-
const inputValue = 'foobar';
213-
component.type(inputControl, inputValue);
232+
test('input is readonly', async () => {
233+
const componentEvents = {
234+
onInput: jest.fn(),
235+
onChange: jest.fn(),
236+
onKeyDown: jest.fn(),
237+
onKeyPress: jest.fn(),
238+
onKeyUp: jest.fn(),
239+
};
214240

215-
expect(componentProperties.onKeyDown).toHaveBeenCalledTimes(inputValue.length);
241+
const component = await render(FixtureComponent, {
242+
componentProperties: {
243+
readonly: true,
244+
...componentEvents,
245+
},
246+
});
216247

217-
component.blur(inputControl);
218-
expect(componentProperties.onChange).toBeCalledTimes(0);
248+
const inputControl = component.getByTestId('input') as HTMLInputElement;
249+
const value = 'Hello';
250+
component.type(inputControl, value);
251+
252+
expect(componentEvents.onInput).not.toHaveBeenCalled();
253+
expect(componentEvents.onChange).not.toHaveBeenCalled();
254+
expect(componentEvents.onKeyDown).toHaveBeenCalledTimes(value.length);
255+
expect(componentEvents.onKeyPress).toHaveBeenCalledTimes(value.length);
256+
expect(componentEvents.onKeyUp).toHaveBeenCalledTimes(value.length);
257+
expect(inputControl.value).toBe('');
258+
});
219259

220-
expect(inputControl.value).toBe('');
260+
test('event.preventDefault() is called', async () => {
261+
const componentProperties = {
262+
onChange: jest.fn(),
263+
onKeyDown: jest.fn().mockImplementation(event => event.preventDefault()),
264+
};
265+
266+
const component = await render(FixtureComponent, { componentProperties });
267+
268+
const inputControl = component.getByTestId('input') as HTMLInputElement;
269+
const inputValue = 'foobar';
270+
component.type(inputControl, inputValue);
271+
272+
expect(componentProperties.onKeyDown).toHaveBeenCalledTimes(inputValue.length);
273+
274+
component.blur(inputControl);
275+
expect(componentProperties.onChange).toBeCalledTimes(0);
276+
277+
expect(inputControl.value).toBe('');
278+
});
221279
});
222280

223281
test('can clear an input field', async () => {

0 commit comments

Comments
 (0)