React testing: Change value of input field - reactjs

I can not set the value of input field. What am I doing wrong?
If any more information is needed, please tell me so. Thank you.
describe('SignUpComp', () => {
let signUpComp, node;
beforeEach(() => {
signUpComp = TestUtils.renderIntoDocument(<SignUp />);
node = ReactDOM.findDOMNode(signUpComp);
});
// First name
it('it should trigger error `min chars` if input firstName is too short', () => {
let elements = selectElements('firstName');
TestUtils.Simulate.change(elements.input, { target: { value: 'abc' } });
console.log(elements.input); // I can not see the change
console.log(node); // I can not see the change
expect(elements.error.textContent).to.equal(errorMsg.tooShort('First name', 2));
});
function selectElements(element) {
let input = node.querySelector('#' + element);
let error = node.querySelector('#' + element + '+ p');
return { input, error };
}

I recommend you to take a look at enzyme, it significantly simplifies testing react components.
With enzyme you can do simply:
const form = mount(<MyComponent />);
const input = form.find('input').get(0);
input.value = 'Blah blah';

Related

How to set different data on ajax call for different function - Jest Enzyme

const req = useAxios(getData1).onData((data) => {});
....
....
const req = useAxios(getData2).onData((data) => {});
it('container should be render with component', () => {
const setAxios = (index) => {
let data = {};
if (index === 1) {
dat1 = { 'test' : '123' };
}
if(index === 2) {
data = { 'test' : '345' };
}
useAxios.mockReturnValue(mockUseAxios(withData(data)))
}
getdata1.mockReturnValue(setAxios(1));
getdata2.mockReturnValue(setAxios(2));
const {container} = render(<Component />);
expect(container.firstChild).toHaveClass('overview');
});
The mock is always set to index 2. How do I return 2 different data based on given methods? The mock sets the most recent value and it is not changing.
useAxios.mockReturnValue() mocks only a single return value. If you call it again, it will overwrite the first value (returning 2 instead of 1).
If you know the order it will be called, you can use mockReturnValueOnce() (Docs):
useAxios.mockReturnValueOnce(mockUseAxios(withData(data)))
If the order can change or you don't want to rely on the order, you can use mockImplementation() (Docs). You'll only want to do this once or it will overwrite, so you shouldn't put this in your setAxios function:
useAxios.mockImplementation(param => {
// return based off param
return param === 'hello' ? { 'test': 123 } : { 'test': 456 }
}

How to test formik yup validation error on blur?

I've written the following test:
it('validates the first name cannot be blank', () => {
const { findByLabelText, getByText } = render(<Profile />);
const firstName = findByLabelText('first name');
firstName.value = '';
fireEvent.blur(firstName);
const error = getByText('First name is required');
expect(error).not.toBeNull();
});
After the test runs I get the error:
Unable to find the "window" object for the given node.
How do I get this test to pass?
So it turns out I was setting the value of first name the wrong way. In fact, in this case there is no need to set the first name, since it defaults to ''. The correct test implementation would be this:
it('validates the first name cannot be blank', async () => {
const { getByLabelText, getByText } = render(<Profile />);
const firstName = getByLabelText(/first name/i);
fireEvent.blur(firstName);
let error;
await waitFor(() => {
error = getByText('First name is required');
});
expect(error).not.toBeNull();
});

simulate change not working with trim() enzyme

I was using this test when I had a bug, so I used the trim function for resolve it, and the these test fail, tried in different ways but didn't found the solution
const generalWrapper = shallow(<AddVehiclesTable {...generalProps} />)
const generalInstance = generalWrapper.instance()
describe('onSearchChange', () => {
test('should change the "search" state', () => {
const theFilterValue = 'a new filter value'
generalWrapper.find('.filter-input').simulate('change', { target: { value: theFilterValue } })
const expectedState = Object.assign({}, generalInstance.state)
expectedState.searchValue = { 'target': { 'value': theFilterValue } }
expect(generalInstance.state).toEqual(expectedState)
expect(generalInstance.state.userInteractedWithComponent).toBe(true)
})
})
onSearchChange (searchValue) {
const value = searchValue.trim()
this.setState({ searchValue: value, userInteractedWithComponent: true })
}
Error message
TypeError: searchValue.trim is not a function
Any suggestions
Your function gets the Object as a parameter.
Expose field that you needed
I don't see the whole picture, but can guess that you need something like
onSearchChange ({ target: { value: incomeValue } }) {
const value = incomeValue.trim()
this.setState({ searchValue: value, userInteractedWithComponent: true })
}

<element>.innerText in component method code does not work properly in enzyme testing. Testing react component with Jest+Enzyme(mount())

Using React+Enzyme+Jest
Hello, I used .innerText property to get value of certain element, see line #5 of my code:
_modifyProfileField (event) {
const { currentAgentProfile, agentsDatabase } = this.state;
const eventTarget = event.target;
const agentToMod = currentAgentProfile;
const valueToSave = event.target.innerHTML !=='<br>'
? eventTarget.innerText
: '';
if (agentToMod[eventTarget.id] !== valueToSave) {
const style = eventTarget.id === 'name'
? Styles.nameSaving
: Styles.saving;
eventTarget.classList.add(style);
const hideSaver = setTimeout(() => {
eventTarget.classList.remove(style);
clearTimeout(hideSaver);
}, 300);
agentToMod[eventTarget.id] = valueToSave;
const newData = agentsDatabase.map((agent) => {
return agent.id === agentToMod.id
? agentToMod
: agent;
});
this.setState({
agentsDatabase: newData,
currentAgentProfile: agentToMod
});
document.activeElement.blur();
window.getSelection().removeAllRanges();
}
}
When I try to run this method for testing in enzyme, event.target.innerHTML returns undefined. Changing innerText to innerHTML is unacceptable due to project requirements. Is it possible to make enzyme understand innerText from my code?
Here is my enzyme code:
expect(result
.state()
.currentAgentProfile.Country)
.toBe(country);
for (const location of locations) {
result.find('#Country').node.innerHTML = location.city;
expect(result.find('#Country').text()).toBe(location.city);
result.find('#Country').simulate('keydown', { key: 'Enter', keyCode: 13, which: 13 });
result.find('#Country').simulate('blur');
expect(result
.state()
.currentAgentProfile.Country)
.toBe(location.city);
}
The blur() simulation triggers my method.
See this answer :
innerText vs textContent - getting some errors on Firefox
in short :
var text = elem.textContent || elem.innerText;
so you could do something like this :
const valueToSave = event.target.innerHTML !=='<br>'
? eventTarget.innerText || eventTarget.textContent
: '';

React Redux and side effects explanation

I'm wrapping my forms to provide automatic validation (I don't want to use redux-form).
I want to pass an onSubmit handler which must be fired after every input in form is validated: but how do I wait for form.valid property to turn into true && after wrapping submit was fired? I'm missing some logic here!
//in my Form.js hoc wrapping the forms
#autobind
submit(event) {
event.preventDefault();
this.props.dispatch(syncValidateForm({ formName: this.props.formName, form: this.props.form }));
// ==> what should I do here? Here I know submit button was pressed but state is not updated yet with last dispatch result reduced!
//if(this.props.form.valid)
// this.props.submit();
}
render() {
return (
<form name={this.props.formName} onSubmit={this.submit}>
{ this.props.children }
</form>
);
//action.js validating given input
export const syncValidateInput = ({ formName, input, name, value }) => {
let errors = {<computed object with errors>};
return { type: INPUT_ERRORS, formName, input, name, value: errors };
};
//action.js validating every input in the form
export const syncValidateForm = ({ formName, form }) => {
return dispatch => {
for(let name in form.inputs) {
let input = form.inputs[name];
dispatch(syncValidateInput({ formName, input, name: input.name, value: input.value }));
}
};
};
//in my reducer I have
case INPUT_ERRORS:
let errors = value;
let valid = true;
let errorText = '';
_.each(errors, (value, key) => {
if(value) {
valid = false;
errorText = `${errorText}${key}\n`;
}
});
form.inputs[name].errors = errors;
form.inputs[name].valid = valid;
form.inputs[name].errorText = errorText;
_.each(form.inputs, (input) => form.valid = !!(form.valid && input.valid));
return state;
Help!
Depending on your build config you could use Async/Await for your submit function. Something like
async submit(event) {
event.preventDefault();
const actionResponse = await this.props.dispatch(syncValidateForm({ formName: this.props.formName, form: this.props.form }));
if (actionResponse && this.props.form.valid) { //for example
// finish submission
}
}
And I think you will need to update your syncValidateForm slightly but this should put you on the right path.

Resources