wix-style-react
Version:
wix-style-react
833 lines (699 loc) • 26.9 kB
JavaScript
import React from 'react';
import sinon from 'sinon';
import inputDriverFactory from './Input.driver';
import Input from '.';
import { createDriverFactory } from 'wix-ui-test-utils/driver-factory';
import { inputTestkitFactory, tooltipTestkitFactory } from '../../testkit';
import { inputTestkitFactory as enzymeInputTestkitFactory } from '../../testkit/enzyme';
import {
isTestkitExists,
isEnzymeTestkitExists,
} from '../../test/utils/testkit-sanity';
import { makeControlled, resolveIn } from '../../test/utils';
import { mount } from 'enzyme';
import {
createRendererWithDriver,
cleanup,
render,
} from '../../test/utils/unit';
describe('Input', () => {
const render = createRendererWithDriver(inputDriverFactory);
const createDriver = createDriverFactory(inputDriverFactory);
const ControlledInput = makeControlled(Input);
afterEach(() => {
cleanup();
});
describe('test tooltip', () => {
it('should display the error tooltip on hover', () => {
const driver = createDriver(
<Input error errorMessage="I'm the error message" />,
);
const dataHook = driver.getTooltipDataHook();
const wrapper = driver.getTooltipElement();
const tooltipDriver = tooltipTestkitFactory({ wrapper, dataHook });
tooltipDriver.mouseEnter();
return resolveIn(500).then(() => {
expect(tooltipDriver.getContent()).toBe("I'm the error message");
});
});
describe('tooltipPlacement attribute', () => {
['top', 'bottom', 'left', 'right'].forEach(placement => {
it(`should have a tooltip positioned to the ${placement}`, () => {
const driver = createDriver(
<Input
error
errorMessage="I'm the error message"
theme="amaterial"
tooltipPlacement={placement}
/>,
);
const dataHook = driver.getTooltipDataHook();
const wrapper = driver.getTooltipElement();
const tooltipDriver = tooltipTestkitFactory({ wrapper, dataHook });
tooltipDriver.mouseEnter();
return resolveIn(500).then(() => {
expect(tooltipDriver.getPlacement()).toBe(placement);
});
});
});
});
describe('onTooltipShow attribute (only for amaterial theme for now)', () => {
it('should be called when error tooltip is active', () => {
const onTooltipShow = sinon.spy();
const driver = createDriver(
<Input
theme="amaterial"
error
errorMessage="I'm the error message"
onTooltipShow={onTooltipShow}
/>,
);
const dataHook = driver.getTooltipDataHook();
const wrapper = driver.getTooltipElement();
const tooltipDriver = tooltipTestkitFactory({ wrapper, dataHook });
tooltipDriver.mouseEnter();
return resolveIn(500).then(() => {
expect(onTooltipShow.calledOnce).toBeTruthy();
});
});
it('should be called when help tooltip is active (only for amaterial theme for now)', () => {
const onTooltipShow = sinon.spy();
const driver = createDriver(
<Input
theme="amaterial"
help
helpMessage="I'm the help message"
onTooltipShow={onTooltipShow}
/>,
);
const dataHook = driver.getTooltipDataHook();
const wrapper = driver.getTooltipElement();
const tooltipDriver = tooltipTestkitFactory({ wrapper, dataHook });
tooltipDriver.mouseEnter();
return resolveIn(500).then(() => {
expect(onTooltipShow.calledOnce).toBeTruthy();
});
});
});
});
describe('enterText driver method', () => {
it('passes the name and value attribute', () => {
const onChange = jest.fn();
const props = {
type: 'text',
name: 'gal',
onChange,
};
const driver = createDriver(<Input {...props} />);
driver.enterText('some text');
expect(onChange).toHaveBeenCalledTimes(1);
const eventTarget = onChange.mock.calls[0][0].target;
expect(eventTarget).toEqual({
name: 'gal',
type: 'text',
value: 'some text',
});
});
});
describe('name attribute', () => {
it('should pass down to the wrapped input', () => {
const props = {
name: 'hello',
};
const driver = createDriver(<Input {...props} />);
expect(driver.getName()).toEqual(props.name);
});
});
describe('type attribute', () => {
it('should pass down to the wrapped input', () => {
const props = {
type: 'number',
};
const driver = createDriver(<Input {...props} />);
expect(driver.getType()).toEqual(props.type);
});
});
describe('value attribute', () => {
it('should pass down to the wrapped input', () => {
const props = {
value: 'hello',
onChange: () => {},
};
const driver = createDriver(<Input {...props} />);
expect(driver.getValue()).toEqual(props.value);
});
});
describe('required attribute', () => {
it('should pass down to the wrapped input', () => {
const driver = createDriver(<Input required />);
expect(driver.getRequired()).toBeTruthy();
});
});
describe('autocomplete attribute', () => {
it('should pass down to the wrapped input', () => {
const driver = createDriver(<Input autocomplete="email" />);
expect(driver.getAutocomplete()).toBe('email');
});
});
describe('defaultValue attribute', () => {
it('should pass down to the wrapped input', () => {
const defaultValue = 'hello';
const driver = createDriver(<Input defaultValue={defaultValue} />);
expect(driver.getDefaultValue()).toEqual(defaultValue);
});
});
describe('tabIndex attribute', () => {
it('should pass down to the wrapped input', () => {
const tabIndex = 1;
const driver = createDriver(<Input tabIndex={tabIndex} />);
expect(driver.getTabIndex()).toEqual(tabIndex);
});
});
describe('readOnly attribute', () => {
it('should pass down to the wrapped input', () => {
const driver = createDriver(<Input readOnly />);
expect(driver.getReadOnly()).toBeTruthy();
});
it('should pass down to the wrapped input with default false value', () => {
const driver = createDriver(<Input />);
expect(driver.getReadOnly()).toBeFalsy();
});
});
describe('textOverflow attribute', () => {
it('should pass down to the wrapped input', () => {
const driver = createDriver(<Input textOverflow="ellipsis" />);
expect(driver.getTextOverflow()).toBe('ellipsis');
});
it('should pass down to the wrapped input with default clip value', () => {
const driver = createDriver(<Input />);
expect(driver.getTextOverflow()).toBe('clip');
});
});
describe('`type` prop', () => {
it('should set type attribute', () => {
const driver = createDriver(<Input type="number" />);
expect(driver.getType()).toBe('number');
});
describe('when "number"', () => {
it('should prevent onChange to be called with non numeric values', () => {
const onChange = jest.fn();
const driver = createDriver(
<Input type="number" onChange={onChange} value="2" />,
);
driver.trigger('change', { target: { value: 'a' } });
driver.trigger('keyPress', { target: { key: 'l' } });
expect(driver.getValue()).toEqual('2');
expect(onChange).not.toHaveBeenCalled();
});
});
});
describe('status attribute', () => {
it('deprecated - should display an error icon if error is true', () => {
const driver = createDriver(<Input error />);
expect(driver.hasExclamation()).toBeTruthy();
expect(driver.hasError()).toBeTruthy();
});
it('should display an error icon if status is error', () => {
const driver = createDriver(<Input status={'error'} />);
expect(driver.hasExclamation()).toBeTruthy();
expect(driver.hasError()).toBeTruthy();
});
it('should display a loader icon if status is loading', () => {
const driver = createDriver(<Input status={'loading'} />);
expect(driver.hasLoader()).toBeTruthy();
});
});
describe('help attribute', () => {
it('should display an help icon if help is true', () => {
const driver = createDriver(<Input help />);
expect(driver.hasHelp()).toBeTruthy();
});
});
describe('unit attribute', () => {
it('should the unit text if passed', () => {
const unit = '$';
const driver = createDriver(<Input unit={unit} />);
expect(driver.getUnit()).toEqual(unit);
});
it('should invoke onInputClicked while click on unit', () => {
const onInputClicked = jest.fn();
const driver = createDriver(
<Input unit="$" onInputClicked={onInputClicked} />,
);
driver.clickUnit();
expect(onInputClicked).toBeCalled();
});
it('should not fail while click on unit without passing onInputClicked', () => {
const driver = createDriver(<Input unit="$" />);
expect(() => {
driver.clickUnit();
}).not.toThrowError(/onInputClicked is not a function/);
});
});
describe('magnifyingGlass attribute', () => {
it('should display a magnifying glass icon if magnifyingGlass is true', () => {
const driver = createDriver(<Input magnifyingGlass />);
expect(driver.hasMagnifyingGlass()).toBeTruthy();
});
it('should not display a magnifying glass icon if magnifyingGlass is false', () => {
const driver = createDriver(<Input magnifyingGlass={false} />);
expect(driver.hasMagnifyingGlass()).toBeFalsy();
});
it('should not display a magnifying glass icon if error is true', () => {
const driver = createDriver(<Input magnifyingGlass error />);
expect(driver.hasMagnifyingGlass()).toBeFalsy();
});
it('should invoke onInputClicked while click on magnifying glass icon', () => {
const onInputClicked = jest.fn();
const driver = createDriver(
<Input magnifyingGlass onInputClicked={onInputClicked} />,
);
driver.clickMagnifyingGlass();
expect(onInputClicked).toBeCalled();
});
it('should not fail while click on magnifying glass icon without passing onInputClicked', () => {
const driver = createDriver(<Input magnifyingGlass />);
expect(() => {
driver.clickMagnifyingGlass();
}).not.toThrowError(/onInputClicked is not a function/);
});
});
describe('menuArrow attribute', () => {
it('should display a menu arrow icon if menuArrow is true', () => {
const driver = createDriver(<Input menuArrow />);
expect(driver.hasMenuArrow()).toBeTruthy();
});
it('should not display a menu arrow icon if menuArrow is false', () => {
const driver = createDriver(<Input menuArrow={false} />);
expect(driver.hasMenuArrow()).toBeFalsy();
});
it('should display a menu arrow icon if error is true', () => {
const driver = createDriver(<Input menuArrow error />);
expect(driver.hasMenuArrow()).toBeTruthy();
});
it('should have a narrow error style of arrow is shown', () => {
const driver = createDriver(<Input menuArrow error />);
expect(driver.isNarrowError()).toBeTruthy();
expect(driver.hasExclamation()).toBeTruthy();
});
it('should not display a menu arrow icon if magnifyingGlass is true', () => {
const driver = createDriver(<Input menuArrow magnifyingGlass />);
expect(driver.hasMenuArrow()).toBeFalsy();
});
it('should invoke onInputClicked while click on menu arrow icon', () => {
const onInputClicked = jest.fn();
const driver = createDriver(
<Input menuArrow onInputClicked={onInputClicked} />,
);
driver.clickMenuArrow();
expect(onInputClicked).toBeCalled();
});
it('should not fail while click on menu arrow icon without passing onInputClicked', () => {
const driver = createDriver(<Input menuArrow />);
expect(() => {
driver.clickMenuArrow();
}).not.toThrowError(/onInputClicked is not a function/);
});
});
describe('rtl attribute', () => {
it('should have rtl if rtl prop is true', () => {
const driver = createDriver(<Input rtl />);
expect(driver.isRTL()).toBeTruthy();
});
it('should not have rtl if rtl prop is false', () => {
const driver = createDriver(<Input rtl={false} />);
expect(driver.isRTL()).toBeFalsy();
});
});
describe('onChange attribute', () => {
it('should be called when text is entered to the input', () => {
const onChange = jest.fn();
const event = { target: { value: 'world' } };
const driver = createDriver(<Input onChange={onChange} />);
driver.trigger('change', event);
expect(onChange).toBeCalled();
});
});
describe('onKeyUp attribute', () => {
it('should be called after keybord key got pressed and then released', () => {
const onKeyUp = jest.fn();
const event = { target: { value: 'world' } };
const driver = createDriver(<Input onKeyUp={onKeyUp} />);
driver.trigger('keyUp', event);
expect(onKeyUp).toBeCalled();
});
});
describe('onFocus attribute', () => {
it('should be called when the input gets focused', () => {
const onFocus = jest.fn();
const driver = createDriver(<Input onFocus={onFocus} />);
driver.trigger('focus');
expect(onFocus).toBeCalled();
});
});
describe('onBlur attribute', () => {
it('should be called when the input gets blured', () => {
const onBlur = jest.fn();
const driver = createDriver(<Input onBlur={onBlur} />);
driver.trigger('blur');
expect(onBlur).toBeCalled();
});
});
describe('onKeyDown attribute', () => {
it('should be called when text is entered to the wrapped input', () => {
const onKeyDown = jest.fn();
const event = { keyCode: 40 };
const driver = createDriver(<Input onKeyDown={onKeyDown} />);
driver.trigger('keyDown', event);
expect(onKeyDown).toBeCalled();
});
});
describe('onPaste attribute', () => {
it('should be called when pasting text to the input', () => {
const onPaste = jest.fn();
const driver = createDriver(<Input onPaste={onPaste} />);
driver.trigger('paste');
expect(onPaste).toBeCalled();
});
});
describe('forceFocus attribute', () => {
it('should have focus class on input if forceFocus is true', () => {
const driver = createDriver(<Input forceFocus />);
expect(driver.isFocusedStyle()).toBeTruthy();
});
});
describe('forceHover attribute', () => {
it('should have hover class on input if forceHover is true', () => {
const driver = createDriver(<Input forceHover />);
expect(driver.isHoveredStyle()).toBeTruthy();
});
it('should be hovered if forceFocus is false and forceHover is true', () => {
const driver = createDriver(<Input forceHover forceFocus={false} />);
expect(driver.isHoveredStyle()).toBeTruthy();
});
});
describe('disable attribute', () => {
it('should have disabled class on input if disabled is true', () => {
const driver = createDriver(<Input disabled />);
expect(driver.isDisabled()).toBeTruthy();
});
});
describe('autoFocus attribute', () => {
it('Mounting an input element with autoFocus=false, should give it the focus', () => {
const { driver, rerender } = render(<Input autoFocus={false} />);
expect(driver.isFocus()).toBeFalsy();
rerender(<Input autoFocus />);
expect(driver.isFocus()).toBeFalsy();
});
it('Mounting an input element with autoFocus=true, gives it the focus', () => {
const driver = createDriver(<Input autoFocus />);
expect(driver.isFocus()).toBeTruthy();
});
describe('with value attribute', () => {
const value = 'this is a string';
it('Should focus with cursor located at the end of the value', () => {
const driver = createDriver(<Input autoFocus value={value} />);
expect(driver.getCursorLocation()).toEqual(value.length);
});
});
});
describe('driver.focus', () => {
it('calling driver.focus (wihtout enzyme) should give focus to the input', () => {
const driver = createDriver(<Input autoFocus={false} />);
expect(driver.isFocus()).toBeFalsy();
driver.focus();
expect(driver.isFocus()).toBeTruthy();
});
});
describe('Input.focus', () => {
it('calling driver.focus (with enzyme) with options, should call the Input instance focus method and pass options', () => {
const wrapper = mount(<Input autoFocus={false} dataHook="test" />);
const focusMock = jest.fn();
wrapper.instance().input.focus = focusMock;
wrapper.instance().focus({ preventScroll: true });
expect(focusMock).toHaveBeenCalledWith({ preventScroll: true });
});
});
describe('theme attribute', () => {
it('should set the theme by default to "normal"', () => {
const driver = createDriver(<Input />);
expect(driver.isOfStyle('normal')).toBeTruthy();
});
it('should allowing setting the theme to "paneltitle"', () => {
const driver = createDriver(<Input theme="paneltitle" />);
expect(driver.isOfStyle('paneltitle')).toBeTruthy();
});
it('should allow setting the theme to "material"', () => {
const driver = createDriver(<Input theme="material" />);
expect(driver.isOfStyle('material')).toBeTruthy();
});
it('should allow setting the theme to "flat"', () => {
const driver = createDriver(<Input theme="flat" />);
expect(driver.isOfStyle('flat')).toBeTruthy();
});
it('should allow setting the theme to "flatdark"', () => {
const driver = createDriver(<Input theme="flatdark" />);
expect(driver.isOfStyle('flatdark')).toBeTruthy();
});
});
describe('clearButton attribute', () => {
it('should be displayed when input text is not empty', () => {
const driver = createDriver(<Input value="some value" clearButton />);
expect(driver.hasClearButton()).toBe(true);
});
it('should not be displayed when input text is empty', () => {
const driver = createDriver(<Input value="" clearButton />);
expect(driver.hasClearButton()).toBe(false);
});
it('should clear input and focus it', () => {
const driver = createDriver(
<ControlledInput clearButton value="some value" />,
);
driver.clickClear();
expect(driver.getValue()).toBe('');
expect(driver.isFocus()).toBe(true);
});
it('should trigger onChange on clearing as if input just emptied', () => {
const onChange = jest.fn();
const driver = createDriver(
<Input onChange={onChange} value="some value" clearButton />,
);
driver.clickClear();
expect(onChange).toHaveBeenCalledTimes(1);
expect(onChange.mock.calls[0][0].target.value).toBe('');
});
describe.skip('Uncontrolled', () => {
// TODO
it('should be displayed when using uncontrolled component with defaultValue', () => {
const driver = createDriver(
<Input defaultValue="some value" clearButton />,
);
expect(driver.hasClearButton()).toBe(true);
});
// TODO
it('should be displayed after entering text into empty uncontrolled input', () => {
const driver = createDriver(<Input clearButton />);
driver.enterText('some value');
expect(driver.hasClearButton()).toBe(true);
});
// TODO
it('should clear input when using uncontrolled component', () => {
const driver = createDriver(<Input clearButton />);
driver.enterText('some value');
driver.clickClear();
expect(driver.getValue()).toBe('');
expect(driver.isFocus()).toBe(true);
});
// TODO
it('should be hidden after default value was overridden with some input', () => {
const driver = createDriver(
<Input defaultValue="some default value" clearButton />,
);
expect(driver.hasClearButton()).toBe(true);
driver.clearText();
driver.enterText('new value');
expect(driver.hasClearButton()).toBe(false);
});
});
});
describe('onClear attribute', () => {
it('should display clear-button when input text is not empty', () => {
const driver = createDriver(
<Input value="some value" onClear={() => null} />,
);
expect(driver.hasClearButton()).toBe(true);
});
it('should invoke callback', () => {
const onClear = sinon.spy();
const driver = createDriver(
<Input onClear={onClear} value="some value" />,
);
expect(driver.hasClearButton()).toBe(true);
driver.clickClear();
expect(onClear.calledOnce).toBe(true);
});
});
describe('clear method', () => {
it('should fire onChange one time when onChange implementation fires clear', () => {
class ControlledInputWithRef extends React.Component {
state = {
value: '',
};
constructor(props) {
super(props);
this.handleChange = this.handleChange.bind(this);
}
handleChange(e) {
this.setState({ value: e.target.value });
this.input.clear();
}
render() {
return (
<Input
ref={comp => (this.input = comp)}
value={this.state.value}
onChange={this.handleChange}
dataHook="my-input"
/>
);
}
}
const handleChangeSpy = jest.spyOn(
ControlledInputWithRef.prototype,
'handleChange',
);
const { driver } = render(<ControlledInputWithRef />, 'my-input');
driver.enterText('foo');
expect(handleChangeSpy).toHaveBeenCalledTimes(2);
expect(handleChangeSpy.mock.calls[0][0].target.value).toBe('foo');
expect(handleChangeSpy.mock.calls[1][0].target.value).toBe('');
});
});
describe('prefix attribute', () => {
it('should allow adding a custom prefix component', () => {
const driver = createDriver(
<Input prefix={<div className="my-button" />} />,
);
expect(driver.hasPrefix()).toBeTruthy();
expect(driver.prefixComponentExists('.my-button')).toBeTruthy();
});
it('should add `withPrefix` classname to input', () => {
const driver = createDriver(<Input prefix="hello" />);
expect(driver.hasPrefixClass()).toBeTruthy();
});
});
describe('suffix attribute', () => {
it('should allow adding a custom suffix component', () => {
const driver = createDriver(
<Input suffix={<div className="my-button" />} />,
);
expect(driver.hasSuffix()).toBeTruthy();
expect(driver.suffixComponentExists('.my-button')).toBeTruthy();
});
it('should add `withSuffix` classname to input', () => {
const driver = createDriver(<Input suffix="hello" />);
expect(driver.hasSuffixClass()).toBeTruthy();
});
it('should add `withSuffixes` classname to input when more than 1 suffix applied', () => {
const driver = createDriver(<Input suffix="hello" magnifyingGlass />);
expect(driver.hasSuffixesClass()).toBeTruthy();
});
it('should render menu arrow as the last suffix', () => {
const driver = createDriver(<Input suffix="hello" menuArrow />);
expect(driver.isMenuArrowLast()).toBeTruthy();
});
});
describe('aria attributes', () => {
it('should allow adding a custom aria-label', () => {
const driver = createDriver(<Input ariaLabel="hello" />);
expect(driver.getAriaLabel()).toBe('hello');
});
it('should not have any aria label buy default', () => {
const driver = createDriver(<Input />);
expect(driver.getAriaLabel()).toBeNull;
});
it('should allow adding aria-controls', () => {
const driver = createDriver(<Input ariaControls="id" />);
expect(driver.getAriaControls()).toBe('id');
});
it('should not have any aria controls by default', () => {
const driver = createDriver(<Input />);
expect(driver.getAriaControls()).toBeNull;
});
it('should allow adding aria-describeby', () => {
const driver = createDriver(<Input ariaDescribedby="blabla" />);
expect(driver.getAriaDescribedby()).toBe('blabla');
});
it('should not have any aria-describeby buy default', () => {
const driver = createDriver(<Input />);
expect(driver.getAriaDescribedby()).toBeNull;
});
});
describe('className prop', () => {
it('should set className on root element', () => {
const className = 'foo';
const driver = createDriver(<Input className={className} />);
expect(driver.getRootElementClasses()).toContain(className);
});
it('should NOT affect the native input classes when className passed', () => {
const className = 'foo';
const driver = createDriver(
<Input className={className} suffix={<div className="my-button" />} />,
);
expect(driver.getInputElementClasses()).not.toContain(className);
expect(driver.suffixComponentExists('.my-button')).toBeTruthy();
});
});
});
describe('testkit exists', () => {
afterEach(() => cleanup());
it('should NOT exist', () => {
const dataHook1 = 'hook1';
const dataHook2 = 'hook2';
const value = 'hello';
const onChange = () => {};
const { container } = render(
<Input value={value} onChange={onChange} dataHook={dataHook1} />,
);
const driver = inputTestkitFactory({
wrapper: container,
dataHook: dataHook2,
});
expect(driver.exists()).toBeFalsy();
});
});
describe('testkit', () => {
it('should exist', () => {
const value = 'hello';
const onChange = () => {};
expect(
isTestkitExists(
<Input value={value} onChange={onChange} />,
inputTestkitFactory,
),
).toBe(true);
});
it('should NOT exist', () => {
const value = 'hello';
const onChange = () => {};
expect(
isTestkitExists(
<Input value={value} onChange={onChange} />,
inputTestkitFactory,
),
).toBe(true);
});
});
describe('enzyme testkit', () => {
it('should exist', () => {
const value = 'hello';
const onChange = () => {};
expect(
isEnzymeTestkitExists(
<Input value={value} onChange={onChange} />,
enzymeInputTestkitFactory,
mount,
),
).toBe(true);
});
});