formsy-react/specs/Formsy-spec.js

520 lines
17 KiB
JavaScript
Executable File

import React from 'react';
import TestUtils from 'react-addons-test-utils';
import Formsy from './..';
import TestInput from './utils/TestInput';
import immediate from './utils/immediate';
describe('Formsy', function () {
describe('Setting up a form', function () {
it('should render a form into the document', function () {
const form = TestUtils.renderIntoDocument(<Formsy.Form></Formsy.Form>);
expect(React.findDOMNode(form).tagName).toEqual('FORM');
});
it('should set a class name if passed', function () {
const form = TestUtils.renderIntoDocument( <Formsy.Form className="foo"></Formsy.Form>);
expect(React.findDOMNode(form).className).toEqual('foo');
});
it('should allow for null/undefined children', function (done) {
let model = null;
const TestForm = React.createClass({
render() {
return (
<Formsy.Form onSubmit={(formModel) => (model = formModel)}>
<h1>Test</h1>
{ null }
{ undefined }
<TestInput name="name" value={ 'foo' } />
</Formsy.Form>
);
}
});
const form = TestUtils.renderIntoDocument(<TestForm/>);
immediate(() => {
TestUtils.Simulate.submit(React.findDOMNode(form));
expect(model).toEqual({name: 'foo'});
done();
});
});
it('should allow for inputs being added dynamically', function (done) {
const inputs = [];
let forceUpdate = null;
let model = null;
const TestForm = React.createClass({
componentWillMount() {
forceUpdate = this.forceUpdate.bind(this);
},
render() {
return (
<Formsy.Form onSubmit={(formModel) => (model = formModel)}>
{inputs}
</Formsy.Form>);
}
});
const form = TestUtils.renderIntoDocument(<TestForm/>);
// Wait before adding the input
setTimeout(() => {
inputs.push(<TestInput name="test" value="" key={inputs.length}/>);
forceUpdate(() => {
// Wait for next event loop, as that does the form
immediate(() => {
TestUtils.Simulate.submit(React.findDOMNode(form));
expect(model.test).toBeDefined();
done();
});
});
}, 10);
});
it('should allow dynamically added inputs to update the form-model', function (done) {
const inputs = [];
let forceUpdate = null;
let model = null;
const TestForm = React.createClass({
componentWillMount() {
forceUpdate = this.forceUpdate.bind(this);
},
render() {
return (
<Formsy.Form onSubmit={(formModel) => (model = formModel)}>
{inputs}
</Formsy.Form>);
}
});
const form = TestUtils.renderIntoDocument(<TestForm/>);
// Wait before adding the input
immediate(() => {
inputs.push(<TestInput name="test" key={inputs.length}/>);
forceUpdate(() => {
// Wait for next event loop, as that does the form
immediate(() => {
TestUtils.Simulate.change(TestUtils.findRenderedDOMComponentWithTag(form, 'INPUT'), {target: {value: 'foo'}});
TestUtils.Simulate.submit(React.findDOMNode(form));
expect(model.test).toBe('foo');
done();
});
});
});
});
it('should allow a dynamically updated input to update the form-model', function (done) {
let forceUpdate = null;
let model = null;
const TestForm = React.createClass({
componentWillMount() {
forceUpdate = this.forceUpdate.bind(this);
},
render() {
const input = <TestInput name="test" value={this.props.value} />;
return (
<Formsy.Form onSubmit={(formModel) => (model = formModel)}>
{input}
</Formsy.Form>);
}
});
let form = TestUtils.renderIntoDocument(<TestForm value="foo"/>);
// Wait before changing the input
immediate(() => {
form = TestUtils.renderIntoDocument(<TestForm value="bar"/>);
forceUpdate(() => {
// Wait for next event loop, as that does the form
immediate(() => {
TestUtils.Simulate.submit(React.findDOMNode(form));
expect(model.test).toBe('bar');
done();
});
});
});
});
describe('validations', function () {
let CheckValid, onSubmit, OtherCheckValid;
let isValid;
const TestForm = React.createClass({
getDefaultProps() {
return { inputs: [] };
},
render() {
const builtInputs = this.props.inputs.map((input) => <TestInput { ...input } key={ input.name } />);
return (
<Formsy.Form
onSubmit={(arg1) => onSubmit(arg1)}
onValid={() => (isValid = true)}
onInvalid={() => (isValid = false)}>
{ builtInputs }
</Formsy.Form>
);
}
});
beforeEach(() => {
isValid = true;
CheckValid = jasmine.createSpy('CheckValid');
Formsy.addValidationRule('CheckValid', CheckValid);
OtherCheckValid = jasmine.createSpy('CheckValid');
Formsy.addValidationRule('OtherCheckValid', OtherCheckValid);
onSubmit = jasmine.createSpy('onSubmit');
});
it('should run when the input changes', function () {
const inputs = [{name: 'one', validations: 'CheckValid', value: 'foo'}];
const form = TestUtils.renderIntoDocument(<TestForm inputs={inputs}/>);
const input = TestUtils.findRenderedDOMComponentWithTag(form, 'input');
TestUtils.Simulate.change(React.findDOMNode(input), {target: {value: 'bar'}});
expect(CheckValid).toHaveBeenCalledWith({one: 'bar'}, 'bar', true);
expect(OtherCheckValid).not.toHaveBeenCalled();
});
it('should allow the validation to be changed', function () {
const inputs = [{name: 'one', validations: 'CheckValid', value: 'foo'}];
const form = TestUtils.renderIntoDocument(<TestForm inputs={inputs}/>);
form.setProps({inputs: [{name: 'one', validations: 'OtherCheckValid', value: 'foo'}] });
const input = TestUtils.findRenderedDOMComponentWithTag(form, 'input');
TestUtils.Simulate.change(React.findDOMNode(input), {target: {value: 'bar'}});
expect(OtherCheckValid).toHaveBeenCalledWith({one: 'bar'}, 'bar', true);
});
it('should invalidate a form if dynamically inserted input is invalid', function (done) {
const inputs = [{name: 'one', validations: 'isEmail', value: 'foo@bar.com'}];
const form = TestUtils.renderIntoDocument(<TestForm inputs={inputs}/>);
expect(isValid).toEqual(true);
form.setProps({inputs: [
{name: 'one', validations: 'isEmail', value: 'foo@bar.com'},
{name: 'two', validations: 'isEmail', value: 'foo@bar'}
]}, () => {
immediate(() => {
expect(isValid).toEqual(false);
done();
});
});
});
it('should validate a form when removing an invalid input', function (done) {
const form = TestUtils.renderIntoDocument(<TestForm inputs={ [
{name: 'one', validations: 'isEmail', value: 'foo@bar.com'},
{name: 'two', validations: 'isEmail', value: 'foo@bar'}
] } />);
expect(isValid).toEqual(false);
form.setProps({inputs: [{name: 'one', validations: 'isEmail', value: 'foo@bar.com'}]}, () => {
immediate(() => {
expect(isValid).toEqual(true);
done();
});
});
});
it('runs multiple validations', function () {
const inputs = [{name: 'one', validations: 'CheckValid,OtherCheckValid', value: 'foo'}];
const form = TestUtils.renderIntoDocument(<TestForm inputs={inputs}/>);
const input = TestUtils.findRenderedDOMComponentWithTag(form, 'input');
TestUtils.Simulate.change(React.findDOMNode(input), {target: {value: 'bar'}});
expect(CheckValid).toHaveBeenCalledWith({one: 'bar'}, 'bar', true);
expect(OtherCheckValid).toHaveBeenCalledWith({one: 'bar'}, 'bar', true);
});
});
it('should not trigger onChange when form is mounted', function () {
const hasChanged = jasmine.createSpy('onChange');
const TestForm = React.createClass({
render() {
return <Formsy.Form onChange={hasChanged}></Formsy.Form>;
}
});
TestUtils.renderIntoDocument(<TestForm/>);
expect(hasChanged).not.toHaveBeenCalled();
});
it('should trigger onChange when form element is changed', function () {
const hasChanged = jasmine.createSpy('onChange');
const TestForm = React.createClass({
render() {
return (
<Formsy.Form onChange={hasChanged}>
<TestInput name="foo"/>
</Formsy.Form>
);
}
});
const form = TestUtils.renderIntoDocument(<TestForm/>);
TestUtils.Simulate.change(TestUtils.findRenderedDOMComponentWithTag(form, 'INPUT'), {target: {value: 'bar'}});
expect(hasChanged).toHaveBeenCalled();
});
it('should trigger onChange when new input is added to form', function (done) {
const hasChanged = jasmine.createSpy('onChange');
const inputs = [];
let forceUpdate = null;
const TestForm = React.createClass({
componentWillMount() {
forceUpdate = this.forceUpdate.bind(this);
},
render() {
return (
<Formsy.Form onChange={hasChanged}>
{inputs}
</Formsy.Form>);
}
});
TestUtils.renderIntoDocument(<TestForm/>);
// Wait before adding the input
inputs.push(<TestInput name="test" key={inputs.length}/>);
forceUpdate(() => {
// Wait for next event loop, as that does the form
immediate(() => {
expect(hasChanged).toHaveBeenCalled();
done();
});
});
});
});
describe('Update a form', function () {
it('should allow elements to check if the form is disabled', function (done) {
const TestForm = React.createClass({
getInitialState() { return { disabled: true }; },
enableForm() { this.setState({ disabled: false }); },
render() {
return (
<Formsy.Form onChange={this.onChange} disabled={this.state.disabled}>
<TestInput name="foo"/>
</Formsy.Form>);
}
});
const form = TestUtils.renderIntoDocument(<TestForm/>);
const input = TestUtils.findRenderedComponentWithType(form, TestInput);
expect(input.isFormDisabled()).toBe(true);
form.enableForm();
immediate(() => {
expect(input.isFormDisabled()).toBe(false);
done();
});
});
it('should be possible to pass error state of elements by changing an errors attribute', function (done) {
const TestForm = React.createClass({
getInitialState() { return { validationErrors: { foo: 'bar' } }; },
onChange(values) {
this.setState(values.foo ? { validationErrors: {} } : { validationErrors: {foo: 'bar'} });
},
render() {
return (
<Formsy.Form onChange={this.onChange} validationErrors={this.state.validationErrors}>
<TestInput name="foo"/>
</Formsy.Form>);
}
});
const form = TestUtils.renderIntoDocument(<TestForm/>);
// Wait for update
immediate(() => {
const input = TestUtils.findRenderedComponentWithType(form, TestInput);
expect(input.getErrorMessage()).toBe('bar');
input.setValue('gotValue');
// Wait for update
immediate(() => {
expect(input.getErrorMessage()).toBe(null);
done();
});
});
});
it('should trigger an onValidSubmit when submitting a valid form', function () {
let isCalled = false;
const TestForm = React.createClass({
render() {
return (
<Formsy.Form onValidSubmit={() => (isCalled = true)}>
<TestInput name="foo" validations="isEmail" value="foo@bar.com"/>
</Formsy.Form>);
}
});
const form = TestUtils.renderIntoDocument(<TestForm/>);
const FoundForm = TestUtils.findRenderedComponentWithType(form, TestForm);
TestUtils.Simulate.submit(React.findDOMNode(FoundForm));
expect(isCalled).toBe(true);
});
it('should trigger an onInvalidSubmit when submitting an invalid form', function () {
let isCalled = false;
const TestForm = React.createClass({
render() {
return (
<Formsy.Form onInvalidSubmit={() => (isCalled = true)}>
<TestInput name="foo" validations="isEmail" value="foo@bar"/>
</Formsy.Form>);
}
});
const form = TestUtils.renderIntoDocument(
<TestForm/>
);
const FoundForm = TestUtils.findRenderedComponentWithType(form, TestForm);
TestUtils.Simulate.submit(React.findDOMNode(FoundForm));
expect(isCalled).toBe(true);
});
});
describe("value === false", function () {
let onSubmit;
const TestForm = React.createClass({
render() {
return (
<Formsy.Form onSubmit={(x) => onSubmit(x)}>
<TestInput name="foo" value={ this.props.value } type="checkbox" />
<button type="submit">Save</button>
</Formsy.Form>
);
}
});
beforeEach(() => {
onSubmit = jasmine.createSpy('onSubmit');
});
it("should call onSubmit correctly", function () {
const form = TestUtils.renderIntoDocument(<TestForm value={ false }/>);
TestUtils.Simulate.submit(React.findDOMNode(form));
expect(onSubmit).toHaveBeenCalledWith({foo: false});
});
it("should allow dynamic changes to false", function () {
const form = TestUtils.renderIntoDocument(<TestForm value={ true }/>);
form.setProps({value: false});
TestUtils.Simulate.submit(React.findDOMNode(form));
expect(onSubmit).toHaveBeenCalledWith({foo: false});
});
it("should say the form is submitted", function () {
const form = TestUtils.renderIntoDocument(<TestForm value={ true }/>);
const input = TestUtils.findRenderedComponentWithType(form, TestInput);
expect(input.isFormSubmitted()).toBe(false);
TestUtils.Simulate.submit(React.findDOMNode(form));
expect(input.isFormSubmitted()).toBe(true);
});
it("should be able to reset the form to its pristine state", function () {
const form = TestUtils.renderIntoDocument(<TestForm value={ true }/>);
const input = TestUtils.findRenderedComponentWithType(form, TestInput);
const formsyForm = TestUtils.findRenderedComponentWithType(form, Formsy.Form);
expect(input.getValue()).toBe(true);
form.setProps({value: false});
expect(input.getValue()).toBe(false);
formsyForm.reset();
expect(input.getValue()).toBe(true);
});
it("should be able to reset the form using custom data", function () {
const form = TestUtils.renderIntoDocument(<TestForm value={ true }/>);
const input = TestUtils.findRenderedComponentWithType(form, TestInput);
const formsyForm = TestUtils.findRenderedComponentWithType(form, Formsy.Form);
expect(input.getValue()).toBe(true);
form.setProps({value: false});
expect(input.getValue()).toBe(false);
formsyForm.reset({
foo: 'bar'
});
expect(input.getValue()).toBe('bar');
});
});
describe('.isChanged()', function () {
let onChange;
const TestForm = React.createClass({
getDefaultProps() {
return { inputs: [] };
},
render() {
const builtInputs = this.props.inputs.map((input) => <TestInput {...input} key={input.name}/>);
return (
<Formsy.Form ref="formsy" onChange={ onChange }>
{ builtInputs }
{ this.props.children }
</Formsy.Form>
);
}
});
beforeEach(() => {
onChange = jasmine.createSpy('onChange');
});
it('initially returns false', function () {
const form = TestUtils.renderIntoDocument(<TestForm inputs={ [{name: 'one', value: 'foo'}] }/>);
expect(form.refs.formsy.isChanged()).toEqual(false);
expect(onChange).not.toHaveBeenCalled();
});
it('returns true when changed', function () {
const form = TestUtils.renderIntoDocument(<TestForm inputs={ [{name: 'one', value: 'foo'}] }/>);
const input = TestUtils.findRenderedDOMComponentWithTag(form, 'input');
TestUtils.Simulate.change(React.findDOMNode(input), {target: {value: 'bar'}});
expect(form.refs.formsy.isChanged()).toEqual(true);
expect(onChange).toHaveBeenCalledWith({one: 'bar'}, true);
});
it('returns false if changes are undone', function () {
const form = TestUtils.renderIntoDocument(<TestForm inputs={ [{name: 'one', value: 'foo'}] }/>);
const input = TestUtils.findRenderedDOMComponentWithTag(form, 'input');
TestUtils.Simulate.change(React.findDOMNode(input), {target: {value: 'bar'}});
expect(onChange).toHaveBeenCalledWith({one: 'bar'}, true);
TestUtils.Simulate.change(React.findDOMNode(input), {target: {value: 'foo'}});
expect(form.refs.formsy.isChanged()).toEqual(false);
expect(onChange).toHaveBeenCalledWith({one: 'foo'}, false);
});
});
});