Skip to content

Commit

Permalink
feat: added focusable matcher (#395)
Browse files Browse the repository at this point in the history
  • Loading branch information
reiniergs authored Nov 6, 2018
1 parent edf5d83 commit 906bf66
Show file tree
Hide file tree
Showing 6 changed files with 182 additions and 46 deletions.
98 changes: 98 additions & 0 deletions jestMatchers/__test__/toBeFocusable.spec.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
/* eslint-disable react/prop-types, react/no-multi-comp */
import React, { Component } from 'react';
import { mount } from 'enzyme';
import toBeFocusable from './../toBeFocusable';

const Button = () => <button />;
const Input = (props) => {
const { onClick, onFocus, onBlur } = props;
return <input onClick={onClick} onFocus={onFocus} onBlur={onBlur} />;
};
class ButtonIcon extends Component {
constructor(props) {
super(props);
this.buttonRef = React.createRef();
}

focus() {
this.buttonRef.current.focus();
}

click() {
this.buttonRef.current.click();
}

blur() {
this.buttonRef.current.blur();
}

render() {
return <button ref={this.buttonRef} />;
}
}
class FocusableInput extends Component {
constructor(props) {
super(props);
this.buttonRef = React.createRef();
}

focus() {
this.buttonRef.current.focus();
}

click() {
this.buttonRef.current.click();
}

blur() {
this.buttonRef.current.blur();
}

render() {
const { onClick, onFocus, onBlur } = this.props;
return <button ref={this.buttonRef} onClick={onClick} onFocus={onFocus} onBlur={onBlur} />;
}
}
const components = [
mount(<Button />),
mount(<Input />),
mount(<ButtonIcon />),
];

describe('toBeFocusable', () => {
it('should return the right data when any component is passed', () => {
expect(toBeFocusable()).toEqual({
message: expect.any(Function),
pass: false,
});
});
it('should return the right message when any component is passed', () => {
expect(toBeFocusable().message()).toBe('You need to pass a component returned by mount or shallow enzyme\'s methods to check whether it is focusable or not.');
});
it('should return the right data when the component is not mounted by enzyme', () => {
expect(toBeFocusable(Button)).toEqual({
message: expect.any(Function),
pass: false,
});
});
it('should return the right message when the component is not mounted by enzyme', () => {
expect(toBeFocusable(<Button />).message()).toBe('You need to pass a component returned by mount or shallow enzyme\'s methods to check whether it is focusable or not.');
});
it('should return the right data when the component is not focusable', () => {
components.forEach(component => expect(toBeFocusable(component)).toEqual({
message: expect.any(Function),
pass: false,
}));
});
it('should return the right message when the component is not focusable', () => {
components.forEach(component => expect(toBeFocusable(component).message())
.toBe(`Expected ${component.name()} to be focusable but it is not.`));
});
it('should return the right data when the component is focusable', () => {
const component = mount(<FocusableInput />);
expect(toBeFocusable(component)).toEqual({
message: '',
pass: true,
});
});
});
21 changes: 21 additions & 0 deletions jestMatchers/__test__/utils.spec.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import { eachShouldBeOneOrFunction } from './../utils';

describe('eachShouldBeOneOrFunction', () => {
it('should return true when all items in the array passed are 1 or function', () => {
const results = [1, 1, 1, 'function', 'function'];
expect(eachShouldBeOneOrFunction(results)).toBe(true);
});
it('should return false when one item in the array passed is not 1 or function', () => {
const results = [
[1, 1, 0, 'function', 'function'],
[1, 1, 1, '', 'function'],
[1, 1, 1, 'object', 'function'],
[1, 1, 1, undefined, 'function'],
[2, 1, 1, 'function', 'function'],
[1, 1, null, 'function', 'function'],
[1, 1, [], 'function', 'function'],
[1, 1, 1, {}, 'function'],
];
results.forEach(result => expect(eachShouldBeOneOrFunction(result)).toBe(false));
});
});
50 changes: 50 additions & 0 deletions jestMatchers/toBeFocusable.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
import { eachShouldBeOneOrFunction } from './utils';

function isMountedByEnzyme(component) {
return typeof component.setProps === 'function'
&& typeof component.simulate === 'function'
&& typeof component.instance === 'function';
}

export default function toBeFocusable(component) {
if (component && isMountedByEnzyme(component)) {
const onClickMockFn = jest.fn();
const onBlurMockFn = jest.fn();
const onFocusMockFn = jest.fn();

component.setProps({
onClick: onClickMockFn,
onBlur: onBlurMockFn,
onFocus: onFocusMockFn,
});

component.simulate('click');
component.simulate('blur');
component.simulate('focus');
const instance = component.instance();

const results = [
onClickMockFn.mock.calls.length,
onFocusMockFn.mock.calls.length,
onBlurMockFn.mock.calls.length,
instance ? typeof instance.click : null,
instance ? typeof instance.focus : null,
instance ? typeof instance.blur : null,
];

if (eachShouldBeOneOrFunction(results)) {
return {
message: '',
pass: true,
};
}
return {
message: () => `Expected ${component.name()} to be focusable but it is not.`,
pass: false,
};
}
return {
message: () => 'You need to pass a component returned by mount or shallow enzyme\'s methods to check whether it is focusable or not.',
pass: false,
};
}
5 changes: 5 additions & 0 deletions jestMatchers/utils.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
/* eslint-disable import/prefer-default-export */

export function eachShouldBeOneOrFunction(results) {
return results.every(result => result === 1 || result === 'function');
}
6 changes: 5 additions & 1 deletion setupTests.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,11 @@
import { configure } from 'enzyme';
import Adapter from 'enzyme-adapter-react-16';
import { toHaveNoViolations } from 'jest-axe';
import toBeFocusable from './jestMatchers/toBeFocusable';

configure({ adapter: new Adapter() });

expect.extend(toHaveNoViolations);
expect.extend({
...toHaveNoViolations,
toBeFocusable,
});
48 changes: 3 additions & 45 deletions src/components/Button/__test__/button.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,53 +4,11 @@ import Button from './../index';
import Spinner from '../../Spinner';

describe('<Button/>', () => {
it('should call onClick function when someone click over', () => {
const onClickMockFn = jest.fn();
it('should be focusable', () => {
const component = mount(
<Button label="Button label" onClick={onClickMockFn} />,
<Button label="button label" />,
);

component.simulate('click');
expect(onClickMockFn.mock.calls.length).toBe(1);
});
it('should call onBlur function when it lost the focus', () => {
const onBlurMockFn = jest.fn();
const component = mount(
<Button label="Button label" onBlur={onBlurMockFn} />,
);

component.simulate('blur');
expect(onBlurMockFn.mock.calls.length).toBe(1);
});
it('should call onFocus function when it gets the focus', () => {
const onFocusMockFn = jest.fn();
const component = mount(
<Button label="Button label" onFocus={onFocusMockFn} />,
);

component.simulate('focus');
expect(onFocusMockFn.mock.calls.length).toBe(1);
});
it('should be defined the click method', () => {
const component = mount(
<Button label="OK" />,
);

expect(typeof component.instance().click).toBe('function');
});
it('should be defined the focus method', () => {
const component = mount(
<Button label="OK" />,
);

expect(typeof component.instance().focus).toBe('function');
});
it('should be defined the blur method', () => {
const component = mount(
<Button label="OK" />,
);

expect(typeof component.instance().blur).toBe('function');
expect(component).toBeFocusable();
});
it('should focus the button when the focus method is called', () => {
const component = mount(
Expand Down

0 comments on commit 906bf66

Please sign in to comment.