Skip to content

Commit

Permalink
fix: type should not type in readonly or disabled fields (#51)
Browse files Browse the repository at this point in the history
  • Loading branch information
timdeschryver authored Sep 21, 2019
1 parent ce4416f commit ced7046
Show file tree
Hide file tree
Showing 2 changed files with 94 additions and 27 deletions.
37 changes: 23 additions & 14 deletions projects/testing-library/src/lib/user-events/type.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,11 +38,18 @@ export function createType(fireEvent: FireFunction & FireObject) {

return async function type(element: HTMLElement, value: string | number, options?: TypeOptions) {
const { allAtOnce = false, delay = 0 } = options || {};
const initialValue = (element as HTMLInputElement).value;
const inputElement = element as HTMLInputElement;
const initialValue = inputElement.value;

if (inputElement.disabled) {
return;
}

if (allAtOnce || value === '') {
fireEvent.input(element, { target: { value } });
element.addEventListener('blur', createFireChangeEvent(initialValue));
if (!inputElement.readOnly) {
fireEvent.input(inputElement, { target: { value } });
}
inputElement.addEventListener('blur', createFireChangeEvent(initialValue));
return;
}

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

const downEvent = fireEvent.keyDown(element, {
const downEvent = fireEvent.keyDown(inputElement, {
key: key,
keyCode: keyCode,
which: keyCode,
});

if (downEvent) {
const pressEvent = fireEvent.keyPress(element, {
const pressEvent = fireEvent.keyPress(inputElement, {
key: key,
keyCode,
charCode: keyCode,
});

if (pressEvent) {
actuallyTyped += key;
fireEvent.input(element, {
target: {
value: actuallyTyped,
},
bubbles: true,
cancelable: true,
});
if (!inputElement.readOnly) {
fireEvent.input(inputElement, {
target: {
value: actuallyTyped,
},
bubbles: true,
cancelable: true,
});
}
}
}

fireEvent.keyUp(element, {
fireEvent.keyUp(inputElement, {
key: key,
keyCode: keyCode,
which: keyCode,
});
}

element.addEventListener('blur', createFireChangeEvent(initialValue));
inputElement.addEventListener('blur', createFireChangeEvent(initialValue));
};
}
84 changes: 71 additions & 13 deletions projects/testing-library/tests/user-events/type.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -178,13 +178,15 @@ describe('options', () => {
});
});

test('does not type when event.preventDefault() is called', async () => {
describe('does not type when ', () => {
@Component({
selector: 'fixture',
template: `
<input
type="text"
data-testid="input"
[disabled]="disabled"
[readonly]="readonly"
(input)="onInput($event)"
(change)="onChange($event)"
(keydown)="onKeyDown($event)"
Expand All @@ -194,30 +196,86 @@ test('does not type when event.preventDefault() is called', async () => {
`,
})
class FixtureComponent {
@Input() disabled = false;
@Input() readonly = false;

onInput($event) {}
onChange($event) {}
onKeyDown($event) {}
onKeyPress($event) {}
onKeyUp($event) {}
}

const componentProperties = {
onChange: jest.fn(),
onKeyDown: jest.fn().mockImplementation(event => event.preventDefault()),
};
test('input is disabled', async () => {
const componentEvents = {
onInput: jest.fn(),
onChange: jest.fn(),
onKeyDown: jest.fn(),
onKeyPress: jest.fn(),
onKeyUp: jest.fn(),
};

const component = await render(FixtureComponent, {
componentProperties: {
disabled: true,
...componentEvents,
},
});

const inputControl = component.getByTestId('input') as HTMLInputElement;
component.type(inputControl, 'Hello');

const component = await render(FixtureComponent, { componentProperties });
Object.values(componentEvents).forEach(evt => expect(evt).not.toHaveBeenCalled());
expect(inputControl.value).toBe('');
});

const inputControl = component.getByTestId('input') as HTMLInputElement;
const inputValue = 'foobar';
component.type(inputControl, inputValue);
test('input is readonly', async () => {
const componentEvents = {
onInput: jest.fn(),
onChange: jest.fn(),
onKeyDown: jest.fn(),
onKeyPress: jest.fn(),
onKeyUp: jest.fn(),
};

expect(componentProperties.onKeyDown).toHaveBeenCalledTimes(inputValue.length);
const component = await render(FixtureComponent, {
componentProperties: {
readonly: true,
...componentEvents,
},
});

component.blur(inputControl);
expect(componentProperties.onChange).toBeCalledTimes(0);
const inputControl = component.getByTestId('input') as HTMLInputElement;
const value = 'Hello';
component.type(inputControl, value);

expect(componentEvents.onInput).not.toHaveBeenCalled();
expect(componentEvents.onChange).not.toHaveBeenCalled();
expect(componentEvents.onKeyDown).toHaveBeenCalledTimes(value.length);
expect(componentEvents.onKeyPress).toHaveBeenCalledTimes(value.length);
expect(componentEvents.onKeyUp).toHaveBeenCalledTimes(value.length);
expect(inputControl.value).toBe('');
});

expect(inputControl.value).toBe('');
test('event.preventDefault() is called', async () => {
const componentProperties = {
onChange: jest.fn(),
onKeyDown: jest.fn().mockImplementation(event => event.preventDefault()),
};

const component = await render(FixtureComponent, { componentProperties });

const inputControl = component.getByTestId('input') as HTMLInputElement;
const inputValue = 'foobar';
component.type(inputControl, inputValue);

expect(componentProperties.onKeyDown).toHaveBeenCalledTimes(inputValue.length);

component.blur(inputControl);
expect(componentProperties.onChange).toBeCalledTimes(0);

expect(inputControl.value).toBe('');
});
});

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

0 comments on commit ced7046

Please sign in to comment.