Reputation: 1319
I'm making some basic form interaction tests
const setup = (errors=false) => {
let props = {
budget: {}, errors: {},
onChange: () => {},
onSave: () => {}
};
if (errors === true) {
props.errors.budgetCategory= "error";
}
return shallow(<AddBudgetForm {...props} />);
};
describe("Fluctuating errors based on input values", () => {
it("Renders an error when a number is present in the cost field", () => {
let wrapper = setup();
wrapper.find('[name="budgetCategory"]').simulate('change', { target: { value: '7' } });
console.log(wrapper.find('[name="budgetCategory"]').props());
});
});
I analyze the props on the console and see that the value
field is still undefined...
{ className: 'form-control',
placeholder: 'Enter Name of Budget Category',
onChange: [Function: onChange],
value: undefined,
name: 'budgetCategory' }
Ideally what I am trying to test is that simulating the key presses of something that isn't a number should trigger the onChange handler to propogate errors to the form.
I tried adding a new onChange handler in the setup, but didn't work:
let props = {
budget: {}, errors: {},
onChange: (e) => {props.budget.budgetCategory = e.target.value;},
onSave: () => {}
};
import React, { Component, PropTypes } from 'react';
import Alert from '../common/Alert';
import TextInput from '../common/TextInput';
const renderDays = () => {
return Array(31).fill().map((_, i = 1) => <option key={i+1}>{i+1}</option>);
};
const errorsInForm = errors => {
let error = false;
Object.keys(errors).map(item => {
if (errors[item]) { error = true; }
});
return error;
};
const generateValidationError = error => {
return (
<span style={{color: "red"}}>{error}</span>
);
};
const AddBudgetForm = ({budget, onChange, onSave, errors}) => {
return (
<div name="AddBudgetForm">
<form>
{!errorsInForm(errors) &&
<Alert
name="add-budget-alert"
alertType = "alert alert-info"
fontAwesomeIcon = "fa fa-info"
alertDescription = " Adding a budget is simple. Add a category such as groceries
, allocate $200.00 per month and the day you'd like the budget to reset."
/>
}
{errorsInForm(errors) &&
<Alert
name="add-budget-alert"
alertType = "alert alert-danger"
fontAwesomeIcon = "fa fa-warning"
alertDescription = " There are problems with the form submission. Ensure all values in the form are valid."
/>
}
<TextInput
className="form-control"
placeholder="Enter Name of Budget Category"
onChange={onChange}
value={budget.category}
name="budgetCategory"
/>
{errors.budgetCategory != "" && generateValidationError(errors.budgetCategory)}
<div className="form-group input-group">
<span className="input-group-addon"><i className="fa fa-usd"></i></span>
<input
className="form-control"
placeholder="Monthly Budget Cost"
onChange={onChange}
value={budget.cost}
name="budgetCost"
/>
</div>
{errors.budgetCost != "" && generateValidationError(errors.budgetCost)}
<select
className="form-control"
onChange={onChange}
value={budget.date}
name="budgetDate"
>
<option>Select Day of Month Budget Item is Due</option>
{renderDays()}
</select>
{errors.budgetDate != "" && generateValidationError(errors.budgetDate)}
<br/>
{(!errorsInForm(errors)) &&
<button className="btn btn-primary" type="submit" onClick={() => onSave(budget)}>Add Budget</button>
}
{(errorsInForm(errors)) &&
<button className="btn btn-primary" type="submit" disabled>Fix Form Errors</button>
}
</form>
</div>
);
};
AddBudgetForm.propTypes = {
budget: PropTypes.object,
onChange: PropTypes.func,
onSave: PropTypes.func,
errors: PropTypes.object
};
export default AddBudgetForm;
Upvotes: 0
Views: 389
Reputation: 13529
You are using a selector that targets actual DOM element right wrapper.find('[name="budgetCategory"]')
. I assume that inside TextInput
you have an input that gets the same name
. Why not select the TextInput
directly and call its prop like for example:
wrapper.find('TextInput').prop('onChange')(<some value here>)
Upvotes: 0
Reputation: 1672
From the docs, the .simulate()
method only affects the event prop for the event you are simulating. In this case, the synthetic event args you passed will only be supplied to your onChange()
function. The actual value
props will be unaffected.
To confirm, simply update your custom onChange()
handler to console.log
the event object supplied to it, e.g.
let props = {
budget: {}, errors: {},
onChange: (event) => { console.log(event); },
onSave: () => {}
};
Another gotcha on this, is that normal event bubbling won't happen with this simulation -- make sure to run the .simulate()
method directly on the node you want to trigger the event.
Refactored AddBudgetForm NB: These are just minor suggestions, and are not necessarily the only correct way to do it.
import React, { Component, PropTypes } from 'react';
import Alert from '../common/Alert';
import TextInput from '../common/TextInput';
const renderDays = () => Array(31).fill().map(
(_, i = 1) => <option key={i+1}>{i+1}</option>
);
/**
* Returns true if a key has a non-null value.
* @param {Object} errors - Errors object
* @return {Boolean} Is there an error?
*/
const errorsInForm = errors =>
Object.keys(errors).reduce( (hasError, item) => hasError || item != null, false);
const generateValidationError = error => <span style={{color: "red"}}>{error}</span>;
const AddBudgetForm = ({ budget, onChange, onSave, errors = {} }) => (
<div name="AddBudgetForm">
<form>
{ ! errorsInForm(errors)
? (
<Alert
name="add-budget-alert"
alertType = "alert alert-info"
fontAwesomeIcon = "fa fa-info"
alertDescription = " Adding a budget is simple. Add a category such as groceries
, allocate $200.00 per month and the day you'd like the budget to reset."
/>)
: (
<Alert
name="add-budget-alert"
alertType = "alert alert-danger"
fontAwesomeIcon = "fa fa-warning"
alertDescription = " There are problems with the form submission. Ensure all values in the form are valid."
/>
)
}
<TextInput
className="form-control"
placeholder="Enter Name of Budget Category"
onChange={onChange}
value={budget.category}
name="budgetCategory"
/>
{ errors.budgetCategory != "" &&
generateValidationError(errors.budgetCategory)
}
<div className="form-group input-group">
<span className="input-group-addon"><i className="fa fa-usd"></i></span>
<input
className="form-control"
placeholder="Monthly Budget Cost"
onChange={onChange}
value={budget.cost}
name="budgetCost"
/>
</div>
{ errors.budgetCost != "" &&
generateValidationError(errors.budgetCost)
}
<select
className="form-control"
onChange={onChange}
value={budget.date}
name="budgetDate"
>
<option>Select Day of Month Budget Item is Due</option>
{ renderDays() }
</select>
{ errors.budgetDate != "" &&
generateValidationError(errors.budgetDate)
}
<br/>
{ ! errorsInForm(errors)
? <button className="btn btn-primary" type="submit" onClick={() => onSave(budget)}>Add Budget</button>
: <button className="btn btn-primary" type="submit" disabled>Fix Form Errors</button>
}
</form>
</div>
);
AddBudgetForm.propTypes = {
budget: PropTypes.object,
onChange: PropTypes.func,
onSave: PropTypes.func,
errors: PropTypes.object
};
export default AddBudgetForm;
Upvotes: 1