wix-style-react
Version:
wix-style-react
323 lines (263 loc) • 10.1 kB
JavaScript
import React from 'react';
import PropTypes from 'prop-types';
import { mount } from 'enzyme';
import Button from '../../Button';
import { createDriverFactory } from 'wix-ui-test-utils/driver-factory';
import { withFocusable, focusableStates } from './FocusableHOC';
const focusableDriver = ({ element, eventTrigger }) => ({
focus: () => eventTrigger.focus(element),
blur: () => eventTrigger.blur(element),
hasFocusState: () => !!element.getAttribute('data-focus'),
hasFocusVisibleState: () => !!element.getAttribute('data-focus-visible'),
});
describe('FocusableHOC', () => {
const render = Comp =>
mount(Comp, { attachTo: document.createElement('div') });
// Pure component without state
class PureChildComponent extends React.PureComponent {
constructor(props) {
super(props);
this.id = props.id;
this.boundMethod = this.boundMethod.bind(this);
}
static propTypes = {
id: PropTypes.string,
};
static staticVariable = 'staticVariable';
static staticMethod() {
return 'staticMethod';
}
unboundMethod() {
return 'unboundMethod';
}
boundMethod() {
return this.id;
}
render() {
return (
<div
onFocus={this.props.focusableOnFocus} // eslint-disable-line react/prop-types
onBlur={this.props.focusableOnBlur} // eslint-disable-line react/prop-types
{...focusableStates(this.props)}
>
Hello
</div>
);
}
}
const pureChildDriverFactory = ({ element, eventTrigger }) => {
return {
...focusableDriver({
element,
getFocusableElement: () => element,
eventTrigger,
}),
};
};
const WithFocusableComp = withFocusable(PureChildComponent);
describe('Pure component HOC', () => {
it('should render the wrapped component', () => {
const wrapper = render(<WithFocusableComp />);
expect(wrapper.children().instance()).toBeInstanceOf(PureChildComponent);
});
describe('hoisting', () => {
it('should hoist static methods', () => {
expect(WithFocusableComp.staticMethod()).toEqual('staticMethod');
});
it('should hoist static variables', () => {
expect(WithFocusableComp.staticVariable).toEqual('staticVariable');
});
it('should hoist prototype methods from child to HOC and bind them', () => {
const wrapper = render(<WithFocusableComp id="some_id" />);
expect(wrapper.instance().unboundMethod()).toEqual('unboundMethod');
expect(wrapper.instance().boundMethod()).toEqual('some_id');
});
});
});
describe('Focusable', () => {
const driverFactory = createDriverFactory(pureChildDriverFactory);
const createDriver = Component => {
const driver = driverFactory(Component);
const fireMouseDown = () =>
window.dispatchEvent(new window.Event('mousedown'));
const fireMouseUp = () =>
window.dispatchEvent(new window.Event('mouseup'));
const fireKeyDown = () =>
window.dispatchEvent(new window.Event('keydown'));
const fireKeyUp = () => window.dispatchEvent(new window.Event('keyup'));
const tabOut = () => {
fireKeyDown();
driver.blur();
fireKeyUp();
};
const tabIn = () => {
fireKeyDown();
driver.focus();
fireKeyUp();
};
const click = () => {
fireMouseDown();
driver.focus();
fireMouseUp();
};
return {
...driver,
fireMouseDown,
fireKeyDown,
fireKeyUp,
tabOut,
tabIn,
click,
};
};
const expectKeyboardFocused = (driver, msg) => {
const prefix = msg ? `${msg} - ` : '';
expect(driver.hasFocusState()).toBe(true, `${prefix}hasFocusState`);
expect(driver.hasFocusVisibleState()).toBe(
true,
`${prefix}hasFocusVisibleState`,
);
};
const expectNotFocused = (driver, msg) => {
const prefix = msg ? `${msg} - ` : '';
expect(driver.hasFocusState()).toBe(false, `${prefix}hasFocusState`);
expect(driver.hasFocusVisibleState()).toBe(
false,
`${prefix}hasFocusVisibleState`,
);
};
const expectMouseFocused = (driver, msg) => {
const prefix = msg ? `${msg} - ` : '';
expect(driver.hasFocusState()).toBe(true, `${prefix}hasFocusState`);
expect(driver.hasFocusVisibleState()).toBe(
false,
`${prefix}hasFocusVisibleState`,
);
};
let WithFocusableComp2;
let focusableModule;
beforeEach(() => {
// Reseting modules, in order to reset the FocusableHOC.InputMethod.method state.
jest.resetModules();
focusableModule = require('./FocusableHOC');
WithFocusableComp2 = focusableModule.withFocusable(PureChildComponent);
// TODO: find a way to reset the eventHandlers which are added to the window.
});
it('should not have focus nor focus-visible [given] initial render', () => {
const driver = createDriver(<WithFocusableComp2 />);
expectNotFocused(driver);
});
it('should have focus and focus-visible [when] focused programatically', () => {
const driver = createDriver(<WithFocusableComp2 />);
driver.focus();
// Default input is keyboard
expectKeyboardFocused(driver, 'after focus');
});
it('should have focus and focus-visible [when] tabbed in', () => {
const driver = createDriver(<WithFocusableComp2 />);
driver.tabIn();
expectKeyboardFocused(driver, 'after focus');
});
it('should have focus and focus-visible [when] tabbed in withot keyDown', () => {
// This test case checks a scenario when the focus is on the browser's
// url input, and we press tab. The keyDown is not fired.
const driver = createDriver(<WithFocusableComp2 />);
driver.focus();
driver.fireKeyUp();
expectKeyboardFocused(driver, 'after focus');
});
it('should not have focus nor focus-visible [when] blured programatically [given] keyboard focused', () => {
const driver = createDriver(<WithFocusableComp2 />);
driver.tabIn();
expectKeyboardFocused(driver, 'after focus');
driver.blur();
expectNotFocused(driver, 'after blur');
});
it('should have focus but not focus-visible [when] clicked', () => {
const driver = createDriver(<WithFocusableComp2 />);
driver.click();
expectMouseFocused(driver, 'after click');
});
it('should have focus but not focus-visible [when] focused programatically [given] current input method is mouse', () => {
// This test must be here , after a test which finishes with the input method = mouse.
// this test is only to test that we are reseting the FocusableHOC global state proparely,
// usin the jest.resetModules() call.
const driver = createDriver(<WithFocusableComp2 />);
driver.focus();
expectKeyboardFocused(driver, 'after click');
});
/**
* This test checks that the InpurMethod.method state is updated to `keyboard` after
* is was set to `mouse`.
*/
it('should have focus and focus-visible [when] focused [given] mouseDown and blur', () => {
const driver = createDriver(<WithFocusableComp2 />);
driver.click();
expectMouseFocused(driver, 'after click');
driver.blur();
expectNotFocused(driver, 'after blur');
driver.tabIn();
expectKeyboardFocused(driver, 'after focus');
});
it('should not be focused [when] tabbed out [given] focused by mouse', () => {
const driver = createDriver(<WithFocusableComp />);
driver.click();
expectMouseFocused(driver, 'after click');
driver.tabOut();
expectNotFocused(driver, 'after tab');
});
it('should have focus and focus-visible, when: any keyboard key pressed [given] focused by mouse', () => {
const driver = createDriver(<WithFocusableComp />);
driver.click();
expectMouseFocused(driver, 'after click');
driver.fireKeyDown();
expectKeyboardFocused(driver, 'after pressing space');
});
});
describe('regressions', () => {
class ButtonWithDisabledState extends React.Component {
state = { disabled: false };
handleButtonClick = () => {
this.setState({ disabled: true });
};
render() {
return (
<div>
<Button
dataHook="disabled-button"
disabled={this.state.disabled}
onClick={this.handleButtonClick}
>
click me
</Button>
<input
placeholder="click the button and type here"
onChange={() => this.setState({ disabled: false })}
/>
</div>
);
}
}
it('ISSUE-1721: Fix focusable button in disabled state', () => {
const component = mount(<ButtonWithDisabledState />);
const button = component.find('button');
const input = component.find('input');
/* by default should not have focus and should not be disabled */
expect(button.getDOMNode().disabled).toBeFalsy();
expect(button.getDOMNode().attributes['data-focus']).toBeFalsy();
/* become focused */
button.simulate('focus');
expect(button.getDOMNode().attributes['data-focus']).toBeTruthy();
/* click on button after that, button should become disabled */
button.simulate('click');
expect(button.getDOMNode().disabled).toBeTruthy();
/* bug was here, after button become disabled, focus should disappear */
expect(button.getDOMNode().attributes['data-focus']).toBeFalsy();
/* input change, after that should be disabled === false */
input.simulate('change', { target: { value: '123' } });
expect(button.getDOMNode().disabled).toBeFalsy();
/* bug was here, after input change, button should become unfocused */
expect(button.getDOMNode().attributes['data-focus']).toBeFalsy();
});
});
});