diff --git a/src/Field.test.js b/src/Field.test.js index c2e45d60..77b43648 100644 --- a/src/Field.test.js +++ b/src/Field.test.js @@ -7,7 +7,14 @@ const onSubmitMock = values => {} describe('Field', () => { it('should warn if not used inside a form', () => { + const spy = jest.spyOn(global.console, 'error').mockImplementation(() => {}) TestUtils.renderIntoDocument() + expect(spy).toHaveBeenCalled() + expect(spy).toHaveBeenCalledTimes(1) + expect(spy).toHaveBeenCalledWith( + 'Warning: Field must be used inside of a ReactFinalForm component' + ) + spy.mockRestore() }) it('should resubscribe if name changes', () => { @@ -39,14 +46,14 @@ describe('Field', () => { expect(renderInput).not.toHaveBeenCalled() const dom = TestUtils.renderIntoDocument() expect(renderInput).toHaveBeenCalled() - expect(renderInput).toHaveBeenCalledTimes(2) - expect(renderInput.mock.calls[1][0].input.value).toBe('Odie') + expect(renderInput).toHaveBeenCalledTimes(1) + expect(renderInput.mock.calls[0][0].input.value).toBe('Odie') const button = TestUtils.findRenderedDOMComponentWithTag(dom, 'button') TestUtils.Simulate.click(button) - expect(renderInput).toHaveBeenCalledTimes(4) - expect(renderInput.mock.calls[3][0].input.value).toBe('Garfield') + expect(renderInput).toHaveBeenCalledTimes(3) + expect(renderInput.mock.calls[2][0].input.value).toBe('Garfield') }) it('should not resubscribe if name changes when not inside a
(duh)', () => { @@ -69,6 +76,7 @@ describe('Field', () => { ) } } + const spy = jest.spyOn(global.console, 'error').mockImplementation(() => {}) expect(renderInput).not.toHaveBeenCalled() const dom = TestUtils.renderIntoDocument() expect(renderInput).toHaveBeenCalled() @@ -80,6 +88,12 @@ describe('Field', () => { expect(renderInput).toHaveBeenCalledTimes(2) expect(renderInput.mock.calls[1][0].input.value).toBe('') + expect(spy).toHaveBeenCalled() + expect(spy).toHaveBeenCalledTimes(1) + expect(spy).toHaveBeenCalledWith( + 'Warning: Field must be used inside of a ReactFinalForm component' + ) + spy.mockRestore() }) it('should render via children render function', () => { @@ -94,10 +108,9 @@ describe('Field', () => { ) expect(render).toHaveBeenCalled() - // called twice due to field registration adding touched and visited values - expect(render).toHaveBeenCalledTimes(2) + expect(render).toHaveBeenCalledTimes(1) expect(renderInput).toHaveBeenCalled() - expect(renderInput).toHaveBeenCalledTimes(2) + expect(renderInput).toHaveBeenCalledTimes(1) }) it('should unsubscribe on unmount', () => { @@ -141,29 +154,28 @@ describe('Field', () => { ) expect(render).toHaveBeenCalled() - // called twice due to field registration adding touched and visited values - expect(render).toHaveBeenCalledTimes(2) - expect(render.mock.calls[1][0].active).toBeUndefined() - expect(render.mock.calls[1][0].values.foo).toBeUndefined() + expect(render).toHaveBeenCalledTimes(1) + expect(render.mock.calls[0][0].active).toBeUndefined() + expect(render.mock.calls[0][0].values.foo).toBeUndefined() const input = TestUtils.findRenderedDOMComponentWithTag(dom, 'input') TestUtils.Simulate.focus(input) - expect(render).toHaveBeenCalledTimes(3) - expect(render.mock.calls[2][0].active).toBe('foo') - expect(render.mock.calls[2][0].values.foo).toBeUndefined() + expect(render).toHaveBeenCalledTimes(2) + expect(render.mock.calls[1][0].active).toBe('foo') + expect(render.mock.calls[1][0].values.foo).toBeUndefined() TestUtils.Simulate.change(input, { target: { value: 'bar' } }) - expect(render).toHaveBeenCalledTimes(4) - expect(render.mock.calls[3][0].active).toBe('foo') - expect(render.mock.calls[3][0].values.foo).toBe('bar') + expect(render).toHaveBeenCalledTimes(3) + expect(render.mock.calls[2][0].active).toBe('foo') + expect(render.mock.calls[2][0].values.foo).toBe('bar') TestUtils.Simulate.blur(input) - expect(render).toHaveBeenCalledTimes(5) - expect(render.mock.calls[4][0].active).toBeUndefined() - expect(render.mock.calls[4][0].values.foo).toBe('bar') + expect(render).toHaveBeenCalledTimes(4) + expect(render.mock.calls[3][0].active).toBeUndefined() + expect(render.mock.calls[3][0].values.foo).toBe('bar') }) it("should convert '' to undefined on change", () => { @@ -179,21 +191,20 @@ describe('Field', () => { ) expect(render).toHaveBeenCalled() - // called twice due to field registration adding touched and visited values - expect(render).toHaveBeenCalledTimes(2) - expect(render.mock.calls[1][0].values.foo).toBeUndefined() + expect(render).toHaveBeenCalledTimes(1) + expect(render.mock.calls[0][0].values.foo).toBeUndefined() const input = TestUtils.findRenderedDOMComponentWithTag(dom, 'input') TestUtils.Simulate.change(input, { target: { value: 'bar' } }) - expect(render).toHaveBeenCalledTimes(3) - expect(render.mock.calls[2][0].values.foo).toBe('bar') + expect(render).toHaveBeenCalledTimes(2) + expect(render.mock.calls[1][0].values.foo).toBe('bar') TestUtils.Simulate.change(input, { target: { value: '' } }) - expect(render).toHaveBeenCalledTimes(4) - expect(render.mock.calls[3][0].values.foo).toBeUndefined() + expect(render).toHaveBeenCalledTimes(3) + expect(render.mock.calls[2][0].values.foo).toBeUndefined() }) it('should accept a null parse prop to preserve empty strings', () => { @@ -209,21 +220,20 @@ describe('Field', () => { ) expect(render).toHaveBeenCalled() - // called twice due to field registration adding touched and visited values - expect(render).toHaveBeenCalledTimes(2) - expect(render.mock.calls[1][0].values.foo).toBeUndefined() + expect(render).toHaveBeenCalledTimes(1) + expect(render.mock.calls[0][0].values.foo).toBeUndefined() const input = TestUtils.findRenderedDOMComponentWithTag(dom, 'input') TestUtils.Simulate.change(input, { target: { value: '' } }) - expect(render).toHaveBeenCalledTimes(3) - expect(render.mock.calls[2][0].values.foo).toBe('') + expect(render).toHaveBeenCalledTimes(2) + expect(render.mock.calls[1][0].values.foo).toBe('') TestUtils.Simulate.change(input, { target: { value: 'abc' } }) - expect(render).toHaveBeenCalledTimes(4) - expect(render.mock.calls[3][0].values.foo).toBe('abc') + expect(render).toHaveBeenCalledTimes(3) + expect(render.mock.calls[2][0].values.foo).toBe('abc') }) it('should accept a format function prop', () => { @@ -240,25 +250,24 @@ describe('Field', () => { ) expect(render).toHaveBeenCalled() - // called twice due to field registration adding touched and visited values - expect(render).toHaveBeenCalledTimes(2) - expect(render.mock.calls[1][0].values.foo).toBeUndefined() + expect(render).toHaveBeenCalledTimes(1) + expect(render.mock.calls[0][0].values.foo).toBeUndefined() expect(format).toHaveBeenCalled() - expect(format).toHaveBeenCalledTimes(2) + expect(format).toHaveBeenCalledTimes(1) expect(format.mock.calls[0]).toEqual([undefined, 'foo']) expect(renderInput).toHaveBeenCalled() - expect(renderInput).toHaveBeenCalledTimes(2) + expect(renderInput).toHaveBeenCalledTimes(1) expect(renderInput.mock.calls[0][0].input.value).toBe('format.undefined') renderInput.mock.calls[0][0].input.onChange('bar') - expect(format).toHaveBeenCalledTimes(4) - expect(format.mock.calls[3]).toEqual(['bar', 'foo']) + expect(format).toHaveBeenCalledTimes(3) + expect(format.mock.calls[2]).toEqual(['bar', 'foo']) - expect(renderInput).toHaveBeenCalledTimes(4) - expect(renderInput.mock.calls[3][0].input.value).toBe('format.bar') + expect(renderInput).toHaveBeenCalledTimes(3) + expect(renderInput.mock.calls[2][0].input.value).toBe('format.bar') }) it('should accept a null format prop to preserve undefined values', () => { @@ -276,18 +285,17 @@ describe('Field', () => { ) expect(render).toHaveBeenCalled() - // called twice due to field registration adding touched and visited values - expect(render).toHaveBeenCalledTimes(2) - expect(render.mock.calls[1][0].values.foo).toBeUndefined() + expect(render).toHaveBeenCalledTimes(1) + expect(render.mock.calls[0][0].values.foo).toBeUndefined() expect(renderInput).toHaveBeenCalled() - expect(renderInput).toHaveBeenCalledTimes(2) - expect(renderInput.mock.calls[1][0].input.value).toBeUndefined() + expect(renderInput).toHaveBeenCalledTimes(1) + expect(renderInput.mock.calls[0][0].input.value).toBeUndefined() renderInput.mock.calls[0][0].input.onChange('bar') - expect(renderInput).toHaveBeenCalledTimes(4) - expect(renderInput.mock.calls[3][0].input.value).toBe('bar') + expect(renderInput).toHaveBeenCalledTimes(3) + expect(renderInput.mock.calls[2][0].input.value).toBe('bar') }) it('should provide a value of [] when empty on a select multiple', () => { @@ -322,18 +330,18 @@ describe('Field', () => { ) expect(renderInput).toHaveBeenCalled() - expect(renderInput).toHaveBeenCalledTimes(2) - expect(renderInput.mock.calls[1][0].input.value).toBe('') + expect(renderInput).toHaveBeenCalledTimes(1) + expect(renderInput.mock.calls[0][0].input.value).toBe('') renderInput.mock.calls[0][0].input.onChange('bar') - expect(renderInput).toHaveBeenCalledTimes(4) - expect(renderInput.mock.calls[3][0].input.value).toBe('bar') + expect(renderInput).toHaveBeenCalledTimes(3) + expect(renderInput.mock.calls[2][0].input.value).toBe('bar') renderInput.mock.calls[1][0].input.onChange(null) - expect(renderInput).toHaveBeenCalledTimes(6) - expect(renderInput.mock.calls[5][0].input.value).toBe('') + expect(renderInput).toHaveBeenCalledTimes(5) + expect(renderInput.mock.calls[4][0].input.value).toBe('') }) it('should optionally allow null values', () => { @@ -355,18 +363,18 @@ describe('Field', () => { expect(render).toHaveBeenCalled() // called twice due to field registration adding touched and visited values - expect(render).toHaveBeenCalledTimes(2) - expect(render.mock.calls[1][0].values.foo).toBeUndefined() + expect(render).toHaveBeenCalledTimes(1) + expect(render.mock.calls[0][0].values.foo).toBeUndefined() renderInput.mock.calls[0][0].input.onChange('bar') - expect(render).toHaveBeenCalledTimes(3) - expect(render.mock.calls[2][0].values.foo).toBe('bar') + expect(render).toHaveBeenCalledTimes(2) + expect(render.mock.calls[1][0].values.foo).toBe('bar') renderInput.mock.calls[0][0].input.onChange(null) - expect(render).toHaveBeenCalledTimes(4) - expect(render.mock.calls[3][0].values.foo).toBe(null) + expect(render).toHaveBeenCalledTimes(3) + expect(render.mock.calls[2][0].values.foo).toBe(null) }) it('should not let validate prop bleed through', () => { @@ -404,8 +412,8 @@ describe('Field', () => { expect(input).toHaveBeenCalled() // called twice due to field registration adding touched and visited values - expect(input).toHaveBeenCalledTimes(2) - expect(input.mock.calls[1][0].subscription).toBeUndefined() + expect(input).toHaveBeenCalledTimes(1) + expect(input.mock.calls[0][0].subscription).toBeUndefined() }) it('should allow changing field-level validation function', () => { @@ -485,9 +493,8 @@ describe('Field', () => { ) expect(render).toHaveBeenCalled() - // called twice due to field registration adding touched and visited values - expect(render).toHaveBeenCalledTimes(2) - expect(render.mock.calls[1][0].values.foo).toBe(true) + expect(render).toHaveBeenCalledTimes(1) + expect(render.mock.calls[0][0].values.foo).toBe(true) const input = TestUtils.findRenderedDOMComponentWithTag(dom, 'input') expect(input.checked).toBe(true) @@ -510,9 +517,8 @@ describe('Field', () => { ) expect(render).toHaveBeenCalled() - // called twice due to field registration adding touched and visited values - expect(render).toHaveBeenCalledTimes(2) - expect(render.mock.calls[1][0].values.foo).toEqual(['a', 'b', 'c']) + expect(render).toHaveBeenCalledTimes(1) + expect(render.mock.calls[0][0].values.foo).toEqual(['a', 'b', 'c']) const inputs = TestUtils.scryRenderedDOMComponentsWithTag(dom, 'input') expect(inputs[0].checked).toBe(true) @@ -538,17 +544,16 @@ describe('Field', () => { ) expect(render).toHaveBeenCalled() - // called twice due to field registration adding touched and visited values - expect(render).toHaveBeenCalledTimes(2) - expect(render.mock.calls[1][0].values.foo).toEqual(['a', 'b', 'c']) + expect(render).toHaveBeenCalledTimes(1) + expect(render.mock.calls[0][0].values.foo).toEqual(['a', 'b', 'c']) expect(checkboxA).toHaveBeenCalled() - expect(checkboxA).toHaveBeenCalledTimes(2) - expect(checkboxA.mock.calls[1][0].input.checked).toBe(true) + expect(checkboxA).toHaveBeenCalledTimes(1) + expect(checkboxA.mock.calls[0][0].input.checked).toBe(true) expect(checkboxD).toHaveBeenCalled() - expect(checkboxD).toHaveBeenCalledTimes(2) - expect(checkboxD.mock.calls[1][0].input.checked).toBe(false) + expect(checkboxD).toHaveBeenCalledTimes(1) + expect(checkboxD.mock.calls[0][0].input.checked).toBe(false) }) it('should render radio buttons with checked prop', () => { @@ -568,9 +573,8 @@ describe('Field', () => { ) expect(render).toHaveBeenCalled() - // called twice due to field registration adding touched and visited values - expect(render).toHaveBeenCalledTimes(2) - expect(render.mock.calls[1][0].values.foo).toBe('Bar') + expect(render).toHaveBeenCalledTimes(1) + expect(render.mock.calls[0][0].values.foo).toBe('Bar') const [barInput, bazInput] = TestUtils.scryRenderedDOMComponentsWithTag( dom, diff --git a/src/FormSpy.test.js b/src/FormSpy.test.js index f644b989..1b00d91e 100644 --- a/src/FormSpy.test.js +++ b/src/FormSpy.test.js @@ -16,7 +16,14 @@ const hasFormApi = props => { describe('FormSpy', () => { it('should warn error if not used inside a form', () => { + const spy = jest.spyOn(global.console, 'error').mockImplementation(() => {}) TestUtils.renderIntoDocument(
} />) + expect(spy).toHaveBeenCalled() + expect(spy).toHaveBeenCalledTimes(1) + expect(spy).toHaveBeenCalledWith( + 'Warning: FormSpy must be used inside of a ReactFinalForm component' + ) + spy.mockRestore() }) it('should allow subscribing to everything', () => { @@ -47,7 +54,7 @@ describe('FormSpy', () => { expect(render.mock.calls[1][0].validating).toBe(false) expect(render.mock.calls[1][0].values).toEqual({}) expect(renderInput).toHaveBeenCalled() - expect(renderInput).toHaveBeenCalledTimes(2) + expect(renderInput).toHaveBeenCalledTimes(1) // change value renderInput.mock.calls[0][0].input.onChange('bar') @@ -64,7 +71,7 @@ describe('FormSpy', () => { expect(render.mock.calls[3][0].valid).toBe(true) expect(render.mock.calls[3][0].validating).toBe(false) expect(render.mock.calls[3][0].values).toEqual({ foo: 'bar' }) - expect(renderInput).toHaveBeenCalledTimes(4) + expect(renderInput).toHaveBeenCalledTimes(3) }) it('should resubscribe if subscription changes', () => { @@ -150,39 +157,38 @@ describe('FormSpy', () => { ) expect(render).toHaveBeenCalled() - // called twice due to field registration adding touched and visited values - expect(render).toHaveBeenCalledTimes(2) - hasFormApi(render.mock.calls[1][0]) - expect(render.mock.calls[1][0].dirty).toBe(false) - expect(render.mock.calls[1][0].errors).toBeUndefined() - expect(render.mock.calls[1][0].invalid).toBeUndefined() - expect(render.mock.calls[1][0].pristine).toBeUndefined() - expect(render.mock.calls[1][0].submitFailed).toBeUndefined() - expect(render.mock.calls[1][0].submitSucceeded).toBeUndefined() - expect(render.mock.calls[1][0].submitting).toBeUndefined() - expect(render.mock.calls[1][0].valid).toBeUndefined() - expect(render.mock.calls[1][0].validating).toBeUndefined() - expect(render.mock.calls[1][0].values).toEqual({}) + expect(render).toHaveBeenCalledTimes(1) + hasFormApi(render.mock.calls[0][0]) + expect(render.mock.calls[0][0].dirty).toBe(false) + expect(render.mock.calls[0][0].errors).toBeUndefined() + expect(render.mock.calls[0][0].invalid).toBeUndefined() + expect(render.mock.calls[0][0].pristine).toBeUndefined() + expect(render.mock.calls[0][0].submitFailed).toBeUndefined() + expect(render.mock.calls[0][0].submitSucceeded).toBeUndefined() + expect(render.mock.calls[0][0].submitting).toBeUndefined() + expect(render.mock.calls[0][0].valid).toBeUndefined() + expect(render.mock.calls[0][0].validating).toBeUndefined() + expect(render.mock.calls[0][0].values).toEqual({}) expect(renderInput).toHaveBeenCalled() - expect(renderInput).toHaveBeenCalledTimes(2) + expect(renderInput).toHaveBeenCalledTimes(1) // change value renderInput.mock.calls[0][0].input.onChange('bar') // once because whole form rerendered, and again because state changed - expect(render).toHaveBeenCalledTimes(4) - hasFormApi(render.mock.calls[3][0]) - expect(render.mock.calls[3][0].dirty).toBe(true) - expect(render.mock.calls[3][0].errors).toBeUndefined() - expect(render.mock.calls[3][0].invalid).toBeUndefined() - expect(render.mock.calls[3][0].pristine).toBeUndefined() - expect(render.mock.calls[3][0].submitFailed).toBeUndefined() - expect(render.mock.calls[3][0].submitSucceeded).toBeUndefined() - expect(render.mock.calls[3][0].submitting).toBeUndefined() - expect(render.mock.calls[3][0].valid).toBeUndefined() - expect(render.mock.calls[3][0].validating).toBeUndefined() - expect(render.mock.calls[3][0].values).toEqual({ foo: 'bar' }) - expect(renderInput).toHaveBeenCalledTimes(4) + expect(render).toHaveBeenCalledTimes(3) + hasFormApi(render.mock.calls[2][0]) + expect(render.mock.calls[2][0].dirty).toBe(true) + expect(render.mock.calls[2][0].errors).toBeUndefined() + expect(render.mock.calls[2][0].invalid).toBeUndefined() + expect(render.mock.calls[2][0].pristine).toBeUndefined() + expect(render.mock.calls[2][0].submitFailed).toBeUndefined() + expect(render.mock.calls[2][0].submitSucceeded).toBeUndefined() + expect(render.mock.calls[2][0].submitting).toBeUndefined() + expect(render.mock.calls[2][0].valid).toBeUndefined() + expect(render.mock.calls[2][0].validating).toBeUndefined() + expect(render.mock.calls[2][0].values).toEqual({ foo: 'bar' }) + expect(renderInput).toHaveBeenCalledTimes(3) }) it('should not unsubscribe/resubscribe if not in form', () => { @@ -206,6 +212,7 @@ describe('FormSpy', () => { ) } } + const spy = jest.spyOn(global.console, 'error').mockImplementation(() => {}) expect(render).not.toHaveBeenCalled() const dom = TestUtils.renderIntoDocument() expect(render).toHaveBeenCalled() @@ -215,6 +222,12 @@ describe('FormSpy', () => { TestUtils.Simulate.click(button) expect(render).toHaveBeenCalledTimes(2) + expect(spy).toHaveBeenCalled() + expect(spy).toHaveBeenCalledTimes(1) + expect(spy).toHaveBeenCalledWith( + 'Warning: FormSpy must be used inside of a ReactFinalForm component' + ) + spy.mockRestore() }) it('should unsubscribe on unmount', () => { @@ -259,14 +272,14 @@ describe('FormSpy', () => { ) expect(input).toHaveBeenCalled() - expect(input).toHaveBeenCalledTimes(2) + expect(input).toHaveBeenCalledTimes(1) expect(onChange).toHaveBeenCalled() expect(onChange).toHaveBeenCalledTimes(1) expect(onChange).toHaveBeenCalledWith({ dirty: false }) input.mock.calls[0][0].input.onChange('bar') - expect(input).toHaveBeenCalledTimes(4) + expect(input).toHaveBeenCalledTimes(3) expect(onChange).toHaveBeenCalledTimes(2) expect(onChange).toHaveBeenCalledWith({ dirty: true }) }) diff --git a/src/ReactFinalForm.js b/src/ReactFinalForm.js index 94131ce3..7db309dd 100644 --- a/src/ReactFinalForm.js +++ b/src/ReactFinalForm.js @@ -41,6 +41,7 @@ export default class ReactFinalForm extends React.Component { props: Props state: State form: FormApi + mounted: boolean unsubscriptions: Unsubscribe[] static childContextTypes = { @@ -52,22 +53,15 @@ export default class ReactFinalForm extends React.Component { constructor(props: Props) { super(props) const { - children, - component, debug, decorators, initialValues, mutators, onSubmit, - render, subscription, validate, validateOnBlur } = props - warning( - render || typeof children === 'function' || component, - 'Must specify either a render prop, a render function as children, or a component prop' - ) const config: Config = { debug, initialValues, @@ -76,25 +70,19 @@ export default class ReactFinalForm extends React.Component { validate, validateOnBlur } + this.mounted = false try { this.form = createForm(config) } catch (e) { warning(false, e.message) } - let initialState this.unsubscriptions = [] if (this.form) { - this.unsubscriptions.push( - this.form.subscribe((state: FormState) => { - if (initialState) { - this.notify(state) - } else { - initialState = state - } - }, subscription || all) - ) - } - if (initialState) { + // set initial state + let initialState: FormState + this.form.subscribe((state: FormState) => { + initialState = state + }, subscription || all)() this.state = { state: initialState } } if (decorators) { @@ -110,7 +98,11 @@ export default class ReactFinalForm extends React.Component { } } - notify = (state: FormState) => this.setState({ state }) + notify = (state: FormState) => { + if (this.mounted) { + this.setState({ state }) + } + } handleSubmit = (event?: SyntheticEvent) => { if (event && typeof event.preventDefault === 'function') { @@ -128,8 +120,12 @@ export default class ReactFinalForm extends React.Component { componentDidMount() { if (this.form) { + this.unsubscriptions.push( + this.form.subscribe(this.notify, this.props.subscription || all) + ) this.form.resumeValidation() } + this.mounted = true } componentWillReceiveProps(nextProps: Props) { diff --git a/src/ReactFinalForm.test.js b/src/ReactFinalForm.test.js index c11de6eb..2a85e87d 100644 --- a/src/ReactFinalForm.test.js +++ b/src/ReactFinalForm.test.js @@ -1,4 +1,5 @@ import React from 'react' +import ReactDOMServer from 'react-dom/server' import TestUtils from 'react-dom/test-utils' import Form from './ReactFinalForm' import Field from './Field' @@ -26,15 +27,27 @@ describe('ReactFinalForm', () => { }) it('should print a warning with no render or children specified', () => { + const spy = jest.spyOn(global.console, 'error').mockImplementation(() => {}) TestUtils.renderIntoDocument(
) + expect(spy).toHaveBeenCalled() + expect(spy).toHaveBeenCalledTimes(1) + expect(spy).toHaveBeenCalledWith( + 'Warning: Must specify either a render prop, a render function as children, or a component prop to ReactFinalForm' + ) + spy.mockRestore() }) it('should print a warning with no onSubmit specified', () => { + const spy = jest.spyOn(global.console, 'error').mockImplementation(() => {}) const render = jest.fn(() =>
) expect(render).not.toHaveBeenCalled() TestUtils.renderIntoDocument() expect(render).toHaveBeenCalled() expect(render).toHaveBeenCalledTimes(1) + expect(spy).toHaveBeenCalled() + expect(spy).toHaveBeenCalledTimes(1) + expect(spy).toHaveBeenCalledWith('Warning: No onSubmit function specified') + spy.mockRestore() }) it('should allow render to be a component', () => { @@ -91,18 +104,17 @@ describe('ReactFinalForm', () => { ) expect(render).toHaveBeenCalled() - // called twice due to field registration adding touched and visited values - expect(render).toHaveBeenCalledTimes(2) - expect(render.mock.calls[1][0].dirty).toEqual(false) - expect(typeof render.mock.calls[1][0].handleSubmit).toBe('function') - expect(render.mock.calls[1][0].invalid).toEqual(false) - expect(render.mock.calls[1][0].pristine).toEqual(true) - expect(render.mock.calls[1][0].submitFailed).toEqual(false) - expect(render.mock.calls[1][0].submitSucceeded).toEqual(false) - expect(render.mock.calls[1][0].submitting).toEqual(false) - expect(render.mock.calls[1][0].valid).toEqual(true) - expect(render.mock.calls[1][0].validating).toEqual(false) - expect(render.mock.calls[1][0].values).toEqual({}) + expect(render).toHaveBeenCalledTimes(1) + expect(render.mock.calls[0][0].dirty).toEqual(false) + expect(typeof render.mock.calls[0][0].handleSubmit).toBe('function') + expect(render.mock.calls[0][0].invalid).toEqual(false) + expect(render.mock.calls[0][0].pristine).toEqual(true) + expect(render.mock.calls[0][0].submitFailed).toEqual(false) + expect(render.mock.calls[0][0].submitSucceeded).toEqual(false) + expect(render.mock.calls[0][0].submitting).toEqual(false) + expect(render.mock.calls[0][0].valid).toEqual(true) + expect(render.mock.calls[0][0].validating).toEqual(false) + expect(render.mock.calls[0][0].values).toEqual({}) }) it('should render with a field with a limited subscription', () => { @@ -377,4 +389,19 @@ describe('ReactFinalForm', () => { expect(unsubscribe).toHaveBeenCalled() }) + + it('should work with server-side rendering', () => { + const spy = jest.spyOn(global.console, 'error') + ReactDOMServer.renderToString( + ( + + + + )} + /> + ) + expect(spy).not.toHaveBeenCalled() + }) })