UNPKG

@detools/vue-form

Version:

Form State Management for VueJS

427 lines (333 loc) 13.9 kB
import test from 'tape-async' import sinon from 'sinon' import Vue from 'vue' import { isFunction, has, noop } from 'lodash' import { VueFormStoreParams } from '.' test('Register Form Control', async t => { // Prerequisites const name = 'username' const value = 42 const store = new Vue(VueFormStoreParams) const addFormField = sinon.spy(store, 'addFormField') // Action store.registerFormControl({ name, fieldLevelInitialValue: value }) // Expectations t.ok(addFormField.calledOnce, '"addFormField" should be called once') t.ok(store.formFields.includes(name), '"formFields" should have control name') t.ok(!store.removedFields.includes(name), '"removedFields" should not have control name') }) test('Unregister Form Control', async t => { // Prerequisites const name = 'username' const value = 42 const store = new Vue(VueFormStoreParams) const removeFormField = sinon.spy(store, 'removeFormField') store.registerFormControl({ name, fieldLevelInitialValue: value }) // Action store.removeFormField(name) // Expectations t.ok(removeFormField.calledOnce, '"removeFormField" should be called once') t.ok(!store.formFields.includes(name), '"formFields" should not have control name') t.ok(store.removedFields.includes(name), '"removedFields" should have control name') }) test('Handle Model Change', async t => { // Prerequisites const name = 'username' const value = 42 const store = new Vue(VueFormStoreParams) const { useState } = store.registerFormControl({ name, fieldLevelInitialValue: value }) const handleModelChange = sinon.spy(noop) // Action store.setHandleModelChange(handleModelChange) const [, setValue] = useState() const nextValue = 43 setValue(nextValue) // Expectations t.ok(handleModelChange.calledOnce, '"handleModelChange" should be called once') const expectedState = { [name]: nextValue } t.ok(handleModelChange.calledWith(expectedState), 'state should be passed to "handleModelChange"') }) test('Create "setError" callback', async t => { // Prerequisites const name = 'username' const value = 42 const validators = [] const store = new Vue(VueFormStoreParams) const createSetError = sinon.spy(store, 'createSetError') // Action const { setError } = store.registerFormControl({ name, fieldLevelInitialValue: value, validators, }) setError(validators)(value) // Expectations t.ok(createSetError.calledOnce, '"createSetError" should be called once') t.ok(createSetError.calledWith(name), '"createSetError" should be called with name') t.ok(isFunction(setError), '"setError" is function') t.ok(!has(store.syncErrors, name), '"syncErrors" should be empty') }) test('Create "setAsyncError" callback', async t => { // Prerequisites const name = 'username' const value = 42 const store = new Vue(VueFormStoreParams) const createSetAsyncError = sinon.spy(store, 'createSetAsyncError') // Action const { setAsyncError } = store.registerFormControl({ name, fieldLevelInitialValue: value }) // Expectations t.ok(createSetAsyncError.calledOnce, '"createSetAsyncError" should be called once') t.ok(createSetAsyncError.calledWith(name), '"createSetAsyncError" should be called with name') t.ok(isFunction(setAsyncError), '"setAsyncError" is function') t.ok(!has(store.asyncErrors, name), '"asyncErrors" should be empty') }) test('Create "setValue" callback', async t => { // Prerequisites const name = 'username' const value = 42 const validators = [] const store = new Vue(VueFormStoreParams) const createSetValue = sinon.spy(store, 'createSetValue') // Action const { useState } = store.registerFormControl({ name, fieldLevelInitialValue: value }) const [, setValue] = useState() // Expectations t.ok(createSetValue.calledOnce, '"createSetValue" should be called once') t.ok(createSetValue.calledWith(name), '"createSetValue" should be called with name') t.ok(isFunction(setValue), '"setValue" is function') // Action const [, setValueWithEmptyValidators] = useState(validators) setValueWithEmptyValidators(value) // Expectations t.equal(store.state[name], value, '"setValue" should set value for "username"') t.ok(!has(store.syncErrors, name), '"syncErrors" should be empty') // Prerequisites const errorMessage = `${name} should be equal 43` const nextValidators = [controlValue => (controlValue !== 43 ? errorMessage : undefined)] const nextValue = 50 // Action const [, setValueWithValidators] = useState(nextValidators) setValueWithValidators(nextValue) // Expectations t.equal(store.state[name], nextValue, '"setValue" should set value for "username"') t.ok(has(store.syncErrors, name), '"syncErrors" should have an error message') t.equal(store.syncErrors[name], errorMessage, 'sync error message as expected') t.equal(store.isValid, false, '"isValid" computed property should be false') t.equal( store.allErrors[name], errorMessage, '"allErrors" computed property should have errorMessage' ) }) test('Use normalize to get a different value in store', async t => { // Prerequisites const name = 'username' const value = 42 const normalizedValue = 100 const normalize = ({ name: fieldName }) => ({ value: normalizedValue, name: fieldName }) const store = new Vue(VueFormStoreParams) // Action const { useState } = store.registerFormControl({ name, fieldLevelInitialValue: value, normalize }) const [, setValue] = useState() setValue(value) // Expectations t.equal(store.state[name], normalizedValue, '"setValue" set normalizedValue for "username"') }) test('Create "cleanFormValue" callback', async t => { // Prerequisites const name = 'username' const value = 42 const store = new Vue(VueFormStoreParams) const createCleanFormValue = sinon.spy(store, 'createCleanFormValue') // Action const { cleanFormValue } = store.registerFormControl({ name, fieldLevelInitialValue: value }) // Expectations t.ok(createCleanFormValue.calledOnce, '"createCleanFormValue" should be called once') t.ok(createCleanFormValue.calledWith(name), '"createCleanFormValue" should be called with name') t.ok(isFunction(cleanFormValue), '"cleanFormValue" is function') t.ok(!has(store.syncErrors, name), '"syncErrors" should be empty') t.ok(!has(store.removedFields, name), '"removedFields" should not contain "username') // Action cleanFormValue() // Expectations t.ok(!has(store.state, name), '"state" should not contain "username"') t.ok(!has(store.syncErrors, name), '"syncErrors" should not contain "username"') t.ok(!has(store.asyncErrors, name), '"asyncErrors" should not contain "username"') t.ok(!has(store.touchedFields, name), '"touchedFields" should not contain "username"') t.ok(store.removedFields.includes(name), '"removedFields" should contain "username') }) test('Create "setTouched" callback', async t => { // Prerequisites const name = 'username' const value = 42 const validators = [] const store = new Vue(VueFormStoreParams) const createSetTouched = sinon.spy(store, 'createSetTouched') // Action const { setTouched } = store.registerFormControl({ name, fieldLevelInitialValue: value, validators, }) // Expectations t.ok(createSetTouched.calledOnce, '"createSetTouched" should be called once') t.ok(createSetTouched.calledWith(name), '"createSetTouched" should be called with name') t.ok(isFunction(setTouched), '"setTouched" is function') t.ok(!has(store.syncErrors, name), '"syncErrors" should be empty') // Action setTouched() // Expectations t.ok(has(store.touchedFields, name), '"touchedFields" should contain "username"') t.equal(store.touchedFields[name], true, '"touchedFields" value for "username" should be "true"') }) test('Manage validating state', async t => { // Prerequisites const name = 'username' const value = 42 const promise = Promise.resolve() const store = new Vue(VueFormStoreParams) store.registerFormControl({ name, fieldLevelInitialValue: value }) // Action const off = store.manageValidatingState(name, promise) // Expectations t.equal(store.form.validating, true, '"form" state is "validating" until we call "off" handler') t.equal(store.asyncValidations[name], promise, '"asyncValidations" should contain "promise"') t.equal(store.isDisabled, true, '"isDisabled" computed property should be "true"') t.ok(isFunction(off), '"off" handler is function') // Action off() // Expectations t.equal(store.form.validating, false, '"form" state no more "validating"') t.equal( store.asyncValidations[name], undefined, '"asyncValidations" should not contain "promise" for "username"' ) }) test('Manage submitting state', async t => { // Prerequisites const name = 'username' const value = 42 const store = new Vue(VueFormStoreParams) store.registerFormControl({ name, fieldLevelInitialValue: value }) // Action const off = store.manageSubmittingState() // Expectations t.equal(store.form.submitting, true, '"form" state is "submitting" until we call "off" handler') t.equal(store.isDisabled, true, '"isDisabled" computed property should be "true"') t.ok(isFunction(off), '"off" handler is function') // Action off() // Expectations t.equal(store.form.submitting, false, '"form" state no more "submitting"') }) test('Manage touched fields state', async t => { // Prerequisites const name = 'username' const value = 42 const store = new Vue(VueFormStoreParams) store.registerFormControl({ name, fieldLevelInitialValue: value }) // Action store.manageTouchedFieldsState() // Expectations t.same(store.touchedFields, { [name]: true }, '"touchedFields" should contain "username"') }) test('Reset values', async t => { // Prerequisites const name = 'username' const value = 42 const store = new Vue(VueFormStoreParams) // Action store.setInitialValues({ [name]: value }) const { useState } = store.registerFormControl({ name }) // Expectations t.equal(store.state[name], value, '"state" should contain "username" value') // Action const [, setValue] = useState() setValue(value + 1) // Expectations t.equal(store.state[name], value + 1, '"state" should contain updated "username" value') // Action store.resetValues() // Expectations t.equal(store.state[name], value, '"state" should contain original "username"') }) test('Reinitialize values', async t => { // Prerequisites const name = 'username' const value = 42 const arrayName = 'array' const arrayValue = [{ a: 'b' }] const objectName = 'object' const objectValue = { c: 'd' } const initialValues = { [name]: value, [arrayName]: arrayValue, [objectName]: objectValue, } const store = new Vue(VueFormStoreParams) // Action store.setInitialValues(initialValues) const { useState, setError, validateOnReinitialize } = store.registerFormControl({ name }) store.registerFormControl({ name: arrayName }) store.registerFormControl({ name: objectName }) // Expectations t.equal(store.state[name], value, '"state" should contain "username" value') t.same(store.state[arrayName], arrayValue, '"state" should contain `[{ a: "b" }]` value') t.same(store.state[objectName], objectValue, '"state" should contain `{ c: "d" }` value') // Action const [, setValue] = useState() setValue(value + 1) // Expectations t.equal(store.state[name], value + 1, '"state" should contain updated "username" value') // Prerequisites const errorMessage = `${name} should be equal 43` const validators = [controlValue => (controlValue !== 43 ? errorMessage : undefined)] const reinitializeCallback = sinon.spy(state => { setError(validators)(state[name]) }) validateOnReinitialize(reinitializeCallback) // Action store.reinitializeValues({ ...initialValues, [arrayName]: null, [objectName]: null }) // Expectations t.equal(store.state[name], value, '"state" should contain original "username"') t.same(store.state[arrayName], arrayValue, '"state" should contain `[{ a: "b" }]` value') t.same(store.state[objectName], objectValue, '"state" should contain `{ c: "d" }` value') t.ok(reinitializeCallback.calledOnce, '"reinitializeCallback" should be called') t.ok( reinitializeCallback.calledWith(store.state), '"reinitializeCallback" should be called with store state' ) }) test('Add Form Sync Errors', async t => { // Prerequisites const name = 'username' const value = 42 const errorMessage = `${name} is required` const store = new Vue(VueFormStoreParams) store.registerFormControl({ name, fieldLevelInitialValue: value }) // Action store.addFormSyncErrors({ [name]: errorMessage }) // Expectations t.equal(store.syncErrors[name], errorMessage, '"syncErrors" should contain an error') }) test('Remove Form Field Errors', async t => { // Prerequisites const name = 'username' const value = 42 const store = new Vue(VueFormStoreParams) store.registerFormControl({ name, fieldLevelInitialValue: value }) store.$set(store.syncErrors, name, 'syncError') store.$set(store.asyncErrors, name, 'asyncError') store.$set(store.touchedFields, name, true) // Expectations t.equal(store.syncErrors[name], 'syncError', `"syncErrors" should contain error for "${name}"`) t.equal(store.asyncErrors[name], 'asyncError', `"asyncErrors" should contain error for "${name}"`) t.equal(store.touchedFields[name], true, `"touchedFields" should contain "${name}"`) // Action store.removeFormFieldErrors(name) // Expectations t.ok(!has(store.syncErrors, name), `"syncErrors" should not contain error for "${name}"`) t.ok(!has(store.asyncErrors, name), `"asyncErrors" should not contain error for "${name}"`) t.ok(!has(store.touchedFields, name), `"touchedFields" should not contain "${name}"`) })