React unit test with enzyme, test onBlur functionality - reactjs

I have a component with functionality which validates input element onblur and adds error class if validation fails:
TableRows = (props )=>(
return <input class="inputElement" onBlur="this.validate()" />
)
validate function is as follows:
validate = async ({ target }: ChangeEvent<HTMLInputElement>) => {
try {
const element = target;
this.setState({ loading: true });
const { value } = target;
const match = value.match(/^\d+(?:\.\d{0,2})?$/g);
if (!match || match.length === 0) {
element.className += ' inputError';
} else {
element.className = target.className.replace(' inputError', '');
}
const { data: updatedValues } = await sendforSaving({inputValue: value});
this.setState({ newValues: data });
} finally {
this.setState({ loading: false });
}
};
I am trying to write a unit test with enzyme as follows:
it('should add error class on invalid input onblur', () => {
const mockVal4Test = {
localCurrency: 'USD',
value: '20.02.1',
} as any;
component = shallow(
<TableRows {...defaultProps} initialValues={mockVal4Test} currencyType={CurrencyType.LOCAL} />
);
const myInput = component.find('.inputElement');
myInput.simulate('blur');
expect(saleTarget.hasClass('inputError')).toBe(true);
});
I get the myInput element but after simulating blur I am expecting the 'validate' function to be called and error class "inputError" to be added to the element.

I used a mock event for blur event to pass to simulate. And later the same mock event is used to check the changes, as the blur event changes the passed event object. Here is my solution.
it('should add error class on invalid input for StoreTarget input on blur', () => {
component = shallow(
<TableRows {...defaultProps} initialValues={mockVal4Test} currencyType={CurrencyType.LOCAL} />
)
const mockedEvent = {
target: {
value: '1.2.1.2',
className:'inputClass'
}
} as any;
const myInput = component.find('. inputElement');
myInput.simulate('blur', mockedEvent );
expect(mockedEvent.target.className.includes('inputError')).toBe(true);
});

Related

Why is Enzyme document.activeElement an empty object?

I am emplying an useRef to fix focus to button next to input when change of that input is detected:
const handleUserEshopChange = (eshopId: ID) => {
setEshopIdValue(eshopId)
setEshopNameValue('')
focusRef.current.focus()
...
}
I want to test that the focus has been affixed but document.activeElement is just an empty object:
test('onNewEshopCreate is handled correctly', () => {
const mockOnUserEshopSelect = jest.fn()
const mockOnNewEshopCreate = jest.fn()
const useRefSpy = jest.spyOn(React, 'useRef').mockReturnValueOnce({ current: { focus } })
const eshopStep = mount(
<EshopStep
...
/>
, { attachTo: document.body })
act(() => {
eshopStep.find('ForwardRef(AutocompleteInput)').simulate('change', '...')
eshopStep.find('ForwardRef(ContinueButton)').at(1).simulate('click')
})
expect(mockOnNewEshopCreate).toBeCalledTimes(1)
expect(mockOnNewEshopCreate).toBeCalledWith('...')
expect(mockOnUserEshopSelect).toBeCalledTimes(0)
expect(document.activeElement).toBe(eshopStep.find('.button'))
})
Test fails on the last line, as it expects only an ampty object ( {} ). Why is the activeElement empty?

How do I create Test cases for validation methods in Jestjs

I am new to Jestjs and enzyme framework and I am trying to write test cases for a particular react component and I am little stuck.
export class ProductDetailsForm extends Component{
handleMetaDataDefinition = e => {
const { value, name } = e.target;
if (name === "xmlVersion") {
this.checkSpecialCharacters(value);
}
this.setState(prevState => ({
...prevState,
[name]: value
}));
this.props.setProductDetailsFormValue({
...this.props.productDetailsForm,
[name]: value
});
};
checkSpecialCharacters = value => {
if (!value || value.match(/^[a-zA-Z0-9._-]+$/)) {
this.setState(() => ({ error: '' }));
} else {
this.setState(() => ({
error: `Special characters and operators such as !##$%^&*()+{}:;?|\\[]'"= are not allowed`
}));
}
}
render(){
return(
<div>
<MetaDataDefinition
readOnly={false}
metaData={this.state}
handleMetaDataDefinition={this.handleMetaDataDefinition}
validateVersion={this.validateVersion}
/>
</div>
);
}
}
I have started with the test case, but I am stuck and unable to proceed how to work on the function handleMetaDataDefinition for full coverage including the function checkSpecialCharacters. Below is the code that I started to write for ProductDetailsForm.test.js
let wrapper;
beforeEach(() => {
wrapper = shallow(
<ProductDetailForm />
);
});
test("should call handleMetaDataDefinition", ()=> {
wrapper.find('MetaDataDefinition').props('handleMetaDataDefinition');
});
I have used some part of my actual code and not the whole code, as I need help in this specific part only to write test case for handleMetaDataDefinition and checkSpecialCharacters methods.
There're two possible option how to write your tests.
You can trigger validation from your MetaDataDefinition component and pass there needed data.
test("should call handleMetaDataDefinition", ()=> {
const component = wrapper.find('MetaDataDefinition');
fillYourComponentSomehow();
triggerAnEventSomehow();
/*For example component.find('button').simulate('click');
wrapper.update();// We can wait for updating state differently(if needed i'll take a look to doc.)
expect(wrapper.state()).toBe(stateThatYouExpect);
});
Or you can test it as 'black box'
test("should call handleMetaDataDefinition", ()=> {
const component = wrapper.find('MetaDataDefinition');
component.props().handleMetaDataDefinition(objectForMethod)
wrapper.update();
expect(wrapper.state()).toBe(stateThatYouExpect);
});
If you have HOCs around your component you'll need to find this component by class name
wrapper.find('ProductDetailsForm')
UPDATE
You can test it like
let wrapper;
let setProductDetailsFormValue;
beforeEach(() => {
setProductDetailsFormValue = jest.fn();
wrapper = shallow(
<ProductDetailForm setProductDetailsFormValue={setProductDetailsFormValue} />
);
});
test("should call handleMetaDataDefinition", ()=> {
const testObject = { target: {name: 'xmlVersion', value: '!!!123asd!'}, }
const component = wrapper.find('MetaDataDefinition');
component.props().handleMetaDataDefinition(testObject)
wrapper.update();
expect(wrapper.state().error).toBe('Special characters and operators such as !##$%^&*()+{}:;?|\\[]'"= are not allowed');
expect(wrapper.state()[xmlVersion]).toBe('!!!123asd!');
expect(setProductDetailsFormValue).toBeCalledWith({
[xmlVersion]: '!!!123asd!',
...other fields})
});

Testing if onBlur is called

I've been searching how to do this for 2 hours, but couldn't make it work. I have a class called base text, which renders an input field. I'm trying to test if onBlur function is called. But I can't make the test pass. I'm getting a
TypeError: Cannot read property '_isMockFunction' of undefined
Is there any way to test if onBlur function is called with mock?
BaseText.js
looseFocus = event => {
const { value } = event.target;
const { mustEnter, mustFill, limit } = this.props;
if (mustEnter) {
if (value.length < 1) {
alert('Missing info.');
}
}
if (mustFill && limit > 0) {
if (value.length < limit) {
alert('Missing info.');
}
}
};
render() {
const {
/...constants
} = this.props;
const { value } = this.state;
return visible ? (
<Input
disabled={disabled}
placeholder={text}
name={name}
value={value}
onChange={this.handleChange}
onBlur={this.looseFocus}
style={Styles}
minLength={minLength}
exception={exception}
mustEnter={mustEnter}
// prefix={this.props.prefix}
type={type}
maxLength={limit < 0 ? null : limit}
// menuRef={this.props.menuRef}
// zeroPad={this.props.zeroPad}
/>
) : null;
}
}
BaseText.test.js
const defaultBaseText = shallow(<BaseText />);
describe('BaseText should make a function call on blur.', () => {
it('blur it', () => {
const instance = defaultBaseText.instance();
const spy = jest.spyOn(instance, 'onBlur');
instance.forceUpdate();
const p = defaultBaseText.find('Input');
p.simulate('blur');
expect(spy).toHaveBeenCalled();
});
});
Your component BaseText doesn't have a method called onBlur but looseFocus.
You should try to spy looseFocus on your instance and see if it is called when you simulate a blur event.
A Codesandbox from your code : https://codesandbox.io/s/ovjwnln4o9

Simulate change for textarea Jest test

I have the following component:
render() {
return (
<textarea onChange={this.handlechange} value="initial value" />
)
}
handlechange = (e) => {
console.log(e.currentTarget.value);
}
and the corresponding test that's supposed to check if on change fired correctly or not:
const TEST_PROPS = {
OnChange: jest.fn()
}
it("Fires on change correctly", () => {
const textArea = enzyme.mount(<TextArea {...TEST_PROPS} />);
jest.resetAllMocks();
expect(textArea.find("textarea").simulate("change"));
expect(TEST_PROPS.OnChange).toHaveBeenCalledTimes(1);
expect(TEST_PROPS.OnChange).toHaveBeenLastCalledWith(//what should go here?//);
});
I want to pass in the value of the new target.value once the onchange is fired to the toHaveBeenLastCalledWith function. How can I do this?
simulate event accepts a event obj as a 2nd arg, which you can use it in your 2nd assertion.
const TEST_PROPS = {
OnChange: jest.fn()
}
it("Fires on change correctly", () => {
const textArea = enzyme.mount(<TextArea {...TEST_PROPS} />);
const event = { target: { value: "sometext" } };
jest.resetAllMocks();
expect(textArea.find("textarea").simulate("change", event));
expect(TEST_PROPS.OnChange).toHaveBeenCalledTimes(1);
expect(TEST_PROPS.OnChange).toHaveBeenLastCalledWith(event);
});

Convert React events to Webcomponent events

The React docs provide an example of how to use react inside a webcomponent. But the example provided is trivial, and not enough to learn from. In particular it does not provide information about how to bubble up an event out of a webcomponent.
Suppose that the code started as
const proto = Object.create(HTMLElement.prototype, {
attachedCallback: {
value: function() {
const mountPoint = document.createElement('span');
this.createShadowRoot().appendChild(mountPoint);
const name = this.getAttribute('name');
const url = 'https://www.google.com/search?q=' + encodeURIComponent(name);
ReactDOM.render(<input onchange={....} value={...}></input>, mountPoint);
}
}
});
document.registerElement('x-search', {prototype: proto});
How would this be wired up?
What about instead of using xeact?
Define component:
import xeact, {exposed, dispatchEvent, Component} from 'xeact';
#xeact('search')
export default class Search extends Component {
state = {
value: '',
};
onChange(e) {
const value = e.target.value;
this.setState({
value
}, () => {
dispatchEvent(this, 'change' , {
value
});
});
}
#exposed
get value() {
return this.state.value;
}
render() {
const {value} = this.state;
return <span>
<input value={value} onChange={(e) => {this.onChange(e)}} />
</span>
}
}
Use it from HTML:
<x-search></x-search>
<script>
// get value from `change` event
document.querySelector('x-search').addEventListener('change', function(e) {
console.log(e.detail.value);
});
// get value from getter
document.querySelector('x-search').value;
</script>

Resources