refactor: State management logic & tests
This commit is contained in:
parent
9868d99c19
commit
a1476d9986
@ -26,7 +26,7 @@ export default class Formulario {
|
|||||||
|
|
||||||
private readonly registry: Map<string, FormularioFormInterface>
|
private readonly registry: Map<string, FormularioFormInterface>
|
||||||
|
|
||||||
constructor (options?: FormularioOptions) {
|
public constructor (options?: FormularioOptions) {
|
||||||
this.registry = new Map()
|
this.registry = new Map()
|
||||||
|
|
||||||
this.validationRules = validationRules
|
this.validationRules = validationRules
|
||||||
@ -38,7 +38,7 @@ export default class Formulario {
|
|||||||
/**
|
/**
|
||||||
* Given a set of options, apply them to the pre-existing options.
|
* Given a set of options, apply them to the pre-existing options.
|
||||||
*/
|
*/
|
||||||
extend (extendWith: FormularioOptions): Formulario {
|
public extend (extendWith: FormularioOptions): Formulario {
|
||||||
if (typeof extendWith === 'object') {
|
if (typeof extendWith === 'object') {
|
||||||
this.validationRules = merge(this.validationRules, extendWith.validationRules || {})
|
this.validationRules = merge(this.validationRules, extendWith.validationRules || {})
|
||||||
this.validationMessages = merge(this.validationMessages, extendWith.validationMessages || {})
|
this.validationMessages = merge(this.validationMessages, extendWith.validationMessages || {})
|
||||||
@ -47,7 +47,7 @@ export default class Formulario {
|
|||||||
throw new Error(`[Formulario]: Formulario.extend(): should be passed an object (was ${typeof extendWith})`)
|
throw new Error(`[Formulario]: Formulario.extend(): should be passed an object (was ${typeof extendWith})`)
|
||||||
}
|
}
|
||||||
|
|
||||||
runValidation (id: string): Promise<ViolationsRecord> {
|
public runValidation (id: string): Promise<ViolationsRecord> {
|
||||||
if (!this.registry.has(id)) {
|
if (!this.registry.has(id)) {
|
||||||
throw new Error(`[Formulario]: Formulario.runValidation(): no forms with id "${id}"`)
|
throw new Error(`[Formulario]: Formulario.runValidation(): no forms with id "${id}"`)
|
||||||
}
|
}
|
||||||
@ -57,7 +57,7 @@ export default class Formulario {
|
|||||||
return form.runValidation()
|
return form.runValidation()
|
||||||
}
|
}
|
||||||
|
|
||||||
resetValidation (id: string): void {
|
public resetValidation (id: string): void {
|
||||||
if (!this.registry.has(id)) {
|
if (!this.registry.has(id)) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -71,7 +71,7 @@ export default class Formulario {
|
|||||||
* Used by forms instances to add themselves into a registry
|
* Used by forms instances to add themselves into a registry
|
||||||
* @internal
|
* @internal
|
||||||
*/
|
*/
|
||||||
register (id: string, form: FormularioFormInterface): void {
|
public register (id: string, form: FormularioFormInterface): void {
|
||||||
if (this.registry.has(id)) {
|
if (this.registry.has(id)) {
|
||||||
throw new Error(`[Formulario]: Formulario.register(): id "${id}" is already in use`)
|
throw new Error(`[Formulario]: Formulario.register(): id "${id}" is already in use`)
|
||||||
}
|
}
|
||||||
@ -83,7 +83,7 @@ export default class Formulario {
|
|||||||
* Used by forms instances to remove themselves from a registry
|
* Used by forms instances to remove themselves from a registry
|
||||||
* @internal
|
* @internal
|
||||||
*/
|
*/
|
||||||
unregister (id: string): void {
|
public unregister (id: string): void {
|
||||||
if (this.registry.has(id)) {
|
if (this.registry.has(id)) {
|
||||||
this.registry.delete(id)
|
this.registry.delete(id)
|
||||||
}
|
}
|
||||||
@ -93,7 +93,7 @@ export default class Formulario {
|
|||||||
* Get validation rules by merging any passed in with global rules.
|
* Get validation rules by merging any passed in with global rules.
|
||||||
* @internal
|
* @internal
|
||||||
*/
|
*/
|
||||||
getRules (extendWith: Record<string, ValidationRuleFn> = {}): Record<string, ValidationRuleFn> {
|
public getRules (extendWith: Record<string, ValidationRuleFn> = {}): Record<string, ValidationRuleFn> {
|
||||||
return merge(this.validationRules, extendWith)
|
return merge(this.validationRules, extendWith)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -101,7 +101,7 @@ export default class Formulario {
|
|||||||
* Get validation messages by merging any passed in with global messages.
|
* Get validation messages by merging any passed in with global messages.
|
||||||
* @internal
|
* @internal
|
||||||
*/
|
*/
|
||||||
getMessages (vm: Vue, extendWith: Record<string, ValidationMessageI18NFn|string>): Record<string, ValidationMessageFn> {
|
public getMessages (vm: Vue, extendWith: Record<string, ValidationMessageI18NFn|string>): Record<string, ValidationMessageFn> {
|
||||||
const raw = merge(this.validationMessages || {}, extendWith)
|
const raw = merge(this.validationMessages || {}, extendWith)
|
||||||
const messages: Record<string, ValidationMessageFn> = {}
|
const messages: Record<string, ValidationMessageFn> = {}
|
||||||
|
|
||||||
|
@ -15,11 +15,12 @@ import {
|
|||||||
} from 'vue-property-decorator'
|
} from 'vue-property-decorator'
|
||||||
import {
|
import {
|
||||||
clone,
|
clone,
|
||||||
getNested,
|
get,
|
||||||
has,
|
has,
|
||||||
merge,
|
merge,
|
||||||
setNested,
|
set,
|
||||||
shallowEquals,
|
shallowEquals,
|
||||||
|
unset,
|
||||||
} from '@/utils'
|
} from '@/utils'
|
||||||
|
|
||||||
import PathRegistry from '@/PathRegistry'
|
import PathRegistry from '@/PathRegistry'
|
||||||
@ -86,10 +87,15 @@ export default class FormularioForm extends Vue {
|
|||||||
private register (path: string, field: FormularioFieldInterface): void {
|
private register (path: string, field: FormularioFieldInterface): void {
|
||||||
this.registry.add(path, field)
|
this.registry.add(path, field)
|
||||||
|
|
||||||
const value = getNested(this.modelCopy, path)
|
const value = get(this.modelCopy, path)
|
||||||
|
|
||||||
if (!field.hasModel && this.modelIsDefined && value !== undefined) {
|
if (!field.hasModel && this.modelIsDefined) {
|
||||||
|
if (value !== undefined) {
|
||||||
field.model = value
|
field.model = value
|
||||||
|
} else {
|
||||||
|
this.setFieldValue(path, null)
|
||||||
|
this.emitInput()
|
||||||
|
}
|
||||||
} else if (field.hasModel && !shallowEquals(field.proxy, value)) {
|
} else if (field.hasModel && !shallowEquals(field.proxy, value)) {
|
||||||
this.setFieldValue(path, field.proxy)
|
this.setFieldValue(path, field.proxy)
|
||||||
this.emitInput()
|
this.emitInput()
|
||||||
@ -116,13 +122,11 @@ export default class FormularioForm extends Vue {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Provide('__FormularioForm_set')
|
@Provide('__FormularioForm_set')
|
||||||
private setFieldValue (field: string, value: unknown): void {
|
private setFieldValue (path: string, value: unknown): void {
|
||||||
if (value === undefined) {
|
if (value === undefined) {
|
||||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
this.proxy = unset(this.proxy, path) as Record<string, unknown>
|
||||||
const { [field]: value, ...proxy } = this.proxy
|
|
||||||
this.proxy = proxy
|
|
||||||
} else {
|
} else {
|
||||||
setNested(this.proxy, field, value)
|
this.proxy = set(this.proxy, path, value) as Record<string, unknown>
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -214,8 +218,8 @@ export default class FormularioForm extends Vue {
|
|||||||
}
|
}
|
||||||
|
|
||||||
this.registry.getSubset(path).forEach((field, path) => {
|
this.registry.getSubset(path).forEach((field, path) => {
|
||||||
const oldValue = getNested(this.proxy, path)
|
const oldValue = get(this.proxy, path, null)
|
||||||
const newValue = getNested(state, path)
|
const newValue = get(state, path, null)
|
||||||
|
|
||||||
if (!shallowEquals(newValue, oldValue)) {
|
if (!shallowEquals(newValue, oldValue)) {
|
||||||
this.setFieldValue(path, newValue)
|
this.setFieldValue(path, newValue)
|
||||||
|
@ -7,6 +7,7 @@ import FormularioFieldGroup from '@/FormularioFieldGroup.vue'
|
|||||||
import FormularioForm from '@/FormularioForm.vue'
|
import FormularioForm from '@/FormularioForm.vue'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
|
Formulario,
|
||||||
install (Vue: VueConstructor, options?: FormularioOptions): void {
|
install (Vue: VueConstructor, options?: FormularioOptions): void {
|
||||||
Vue.component('FormularioField', FormularioField)
|
Vue.component('FormularioField', FormularioField)
|
||||||
Vue.component('FormularioFieldGroup', FormularioFieldGroup)
|
Vue.component('FormularioFieldGroup', FormularioFieldGroup)
|
||||||
|
@ -7,10 +7,10 @@ const extractIntOrNaN = (value: string): number => {
|
|||||||
return numeric.toString() === value ? numeric : NaN
|
return numeric.toString() === value ? numeric : NaN
|
||||||
}
|
}
|
||||||
|
|
||||||
const extractPath = (field: string): string[] => {
|
const extractPath = (raw: string): string[] => {
|
||||||
const path = [] as string[]
|
const path = [] as string[]
|
||||||
|
|
||||||
field.split('.').forEach(key => {
|
raw.split('.').forEach(key => {
|
||||||
if (/(.*)\[(\d+)]$/.test(key)) {
|
if (/(.*)\[(\d+)]$/.test(key)) {
|
||||||
path.push(...key.substr(0, key.length - 1).split('[').filter(k => k.length))
|
path.push(...key.substr(0, key.length - 1).split('[').filter(k => k.length))
|
||||||
} else {
|
} else {
|
||||||
@ -21,18 +21,11 @@ const extractPath = (field: string): string[] => {
|
|||||||
return path
|
return path
|
||||||
}
|
}
|
||||||
|
|
||||||
const unsetInRecord = (record: Record<string, unknown>, prop: string): Record<string, unknown> => {
|
export function get (state: unknown, rawOrPath: string|string[], fallback: unknown = undefined): unknown {
|
||||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
const path = typeof rawOrPath === 'string' ? extractPath(rawOrPath) : rawOrPath
|
||||||
const { [prop]: _, ...copy } = record
|
|
||||||
|
|
||||||
return copy
|
|
||||||
}
|
|
||||||
|
|
||||||
export function get (state: unknown, fieldOrPath: string|string[]): unknown {
|
|
||||||
const path = typeof fieldOrPath === 'string' ? extractPath(fieldOrPath) : fieldOrPath
|
|
||||||
|
|
||||||
if (isScalar(state) || path.length === 0) {
|
if (isScalar(state) || path.length === 0) {
|
||||||
return undefined
|
return fallback
|
||||||
}
|
}
|
||||||
|
|
||||||
const key = path.shift() as string
|
const key = path.shift() as string
|
||||||
@ -40,7 +33,7 @@ export function get (state: unknown, fieldOrPath: string|string[]): unknown {
|
|||||||
|
|
||||||
if (!isNaN(index)) {
|
if (!isNaN(index)) {
|
||||||
if (Array.isArray(state) && index >= 0 && index < state.length) {
|
if (Array.isArray(state) && index >= 0 && index < state.length) {
|
||||||
return path.length === 0 ? state[index] : get(state[index], path)
|
return path.length === 0 ? state[index] : get(state[index], path, fallback)
|
||||||
}
|
}
|
||||||
|
|
||||||
return undefined
|
return undefined
|
||||||
@ -49,18 +42,54 @@ export function get (state: unknown, fieldOrPath: string|string[]): unknown {
|
|||||||
if (has(state as Record<string, unknown>, key)) {
|
if (has(state as Record<string, unknown>, key)) {
|
||||||
const values = state as Record<string, unknown>
|
const values = state as Record<string, unknown>
|
||||||
|
|
||||||
return path.length === 0 ? values[key] : get(values[key], path)
|
return path.length === 0 ? values[key] : get(values[key], path, fallback)
|
||||||
}
|
}
|
||||||
|
|
||||||
return undefined
|
return undefined
|
||||||
}
|
}
|
||||||
|
|
||||||
export function unset (state: unknown, fieldOrPath: string|string[]): unknown {
|
export function set (state: unknown, rawOrPath: string|string[], value: unknown): unknown {
|
||||||
|
const path = typeof rawOrPath === 'string' ? extractPath(rawOrPath) : rawOrPath
|
||||||
|
|
||||||
|
if (path.length === 0) {
|
||||||
|
return value
|
||||||
|
}
|
||||||
|
|
||||||
|
const key = path.shift() as string
|
||||||
|
const index = extractIntOrNaN(key)
|
||||||
|
|
||||||
|
if (!isRecordLike(state)) {
|
||||||
|
return set(!isNaN(index) ? [] : {}, [key, ...path], value)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!isNaN(index) && Array.isArray(state)) {
|
||||||
|
const slice = [...state as unknown[]]
|
||||||
|
|
||||||
|
slice[index] = path.length === 0 ? value : set(slice[index], path, value)
|
||||||
|
|
||||||
|
return slice
|
||||||
|
}
|
||||||
|
|
||||||
|
const slice = { ...state as Record<string, unknown> }
|
||||||
|
|
||||||
|
slice[key] = path.length === 0 ? value : set(slice[key], path, value)
|
||||||
|
|
||||||
|
return slice
|
||||||
|
}
|
||||||
|
|
||||||
|
const unsetInRecord = (record: Record<string, unknown>, prop: string): Record<string, unknown> => {
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||||
|
const { [prop]: _, ...copy } = record
|
||||||
|
|
||||||
|
return copy
|
||||||
|
}
|
||||||
|
|
||||||
|
export function unset (state: unknown, rawOrPath: string|string[]): unknown {
|
||||||
if (!isRecordLike(state)) {
|
if (!isRecordLike(state)) {
|
||||||
return state
|
return state
|
||||||
}
|
}
|
||||||
|
|
||||||
const path = typeof fieldOrPath === 'string' ? extractPath(fieldOrPath) : fieldOrPath
|
const path = typeof rawOrPath === 'string' ? extractPath(rawOrPath) : rawOrPath
|
||||||
|
|
||||||
if (path.length === 0) {
|
if (path.length === 0) {
|
||||||
return state
|
return state
|
||||||
@ -70,23 +99,23 @@ export function unset (state: unknown, fieldOrPath: string|string[]): unknown {
|
|||||||
const index = extractIntOrNaN(key)
|
const index = extractIntOrNaN(key)
|
||||||
|
|
||||||
if (!isNaN(index) && Array.isArray(state) && index >= 0 && index < state.length) {
|
if (!isNaN(index) && Array.isArray(state) && index >= 0 && index < state.length) {
|
||||||
const values = (state as unknown[]).slice()
|
const slice = [...state as unknown[]]
|
||||||
|
|
||||||
if (path.length === 0) {
|
if (path.length === 0) {
|
||||||
values.splice(index, 1)
|
slice.splice(index, 1)
|
||||||
} else {
|
} else {
|
||||||
values[index] = unset(values[index], path)
|
slice[index] = unset(slice[index], path)
|
||||||
}
|
}
|
||||||
|
|
||||||
return values
|
return slice
|
||||||
}
|
}
|
||||||
|
|
||||||
if (has(state as Record<string, unknown>, key)) {
|
if (has(state as Record<string, unknown>, key)) {
|
||||||
const values = state as Record<string, unknown>
|
const slice = { ...state as Record<string, unknown> }
|
||||||
|
|
||||||
return path.length === 0
|
return path.length === 0
|
||||||
? unsetInRecord(values, key)
|
? unsetInRecord(slice, key)
|
||||||
: { ...values, [key]: unset(values[key], path) }
|
: { ...slice, [key]: unset(slice[key], path) }
|
||||||
}
|
}
|
||||||
|
|
||||||
return state
|
return state
|
||||||
|
@ -1,70 +1,7 @@
|
|||||||
export { default as clone } from './clone'
|
export { default as clone } from './clone'
|
||||||
export { default as has } from './has'
|
export { default as has } from './has'
|
||||||
export { default as merge } from './merge'
|
export { default as merge } from './merge'
|
||||||
export { get, unset } from './access'
|
export { get, set, unset } from './access'
|
||||||
export { default as regexForFormat } from './regexForFormat'
|
export { default as regexForFormat } from './regexForFormat'
|
||||||
export { default as shallowEquals } from './shallowEquals'
|
export { default as shallowEquals } from './shallowEquals'
|
||||||
export { default as snakeToCamel } from './snakeToCamel'
|
export { default as snakeToCamel } from './snakeToCamel'
|
||||||
|
|
||||||
export function getNested (obj: Record<string, any>, field: string): any {
|
|
||||||
const fieldParts = field.split('.')
|
|
||||||
|
|
||||||
let result: Record<string, any> = obj
|
|
||||||
|
|
||||||
for (const key in fieldParts) {
|
|
||||||
const matches = fieldParts[key].match(/(.+)\[(\d+)\]$/)
|
|
||||||
if (result === undefined) {
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
if (matches) {
|
|
||||||
result = result[matches[1]]
|
|
||||||
|
|
||||||
if (result === undefined) {
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
result = result[matches[2]]
|
|
||||||
} else {
|
|
||||||
result = result[fieldParts[key]]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
|
|
||||||
export function setNested (obj: Record<string, any>, field: string, value: any): void {
|
|
||||||
const fieldParts = field.split('.')
|
|
||||||
|
|
||||||
let subProxy: Record<string, any> = obj
|
|
||||||
for (let i = 0; i < fieldParts.length; i++) {
|
|
||||||
const fieldPart = fieldParts[i]
|
|
||||||
const matches = fieldPart.match(/(.+)\[(\d+)\]$/)
|
|
||||||
|
|
||||||
if (subProxy === undefined) {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
|
|
||||||
if (matches) {
|
|
||||||
if (subProxy[matches[1]] === undefined) {
|
|
||||||
subProxy[matches[1]] = []
|
|
||||||
}
|
|
||||||
subProxy = subProxy[matches[1]]
|
|
||||||
|
|
||||||
if (i === fieldParts.length - 1) {
|
|
||||||
subProxy[matches[2]] = value
|
|
||||||
break
|
|
||||||
} else {
|
|
||||||
subProxy = subProxy[matches[2]]
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if (i === fieldParts.length - 1) {
|
|
||||||
subProxy[fieldPart] = value
|
|
||||||
break
|
|
||||||
} else {
|
|
||||||
// eslint-disable-next-line max-depth
|
|
||||||
if (subProxy[fieldPart] === undefined) {
|
|
||||||
subProxy[fieldPart] = {}
|
|
||||||
}
|
|
||||||
subProxy = subProxy[fieldPart]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
@ -1,6 +1,30 @@
|
|||||||
<template>
|
<template>
|
||||||
<FormularioForm v-model="values">
|
<FormularioForm v-model="values">
|
||||||
<h1>Address list</h1>
|
<h1>Delivery</h1>
|
||||||
|
|
||||||
|
<h3>Customer</h3>
|
||||||
|
|
||||||
|
<FormularioFieldGroup
|
||||||
|
name="customer"
|
||||||
|
class="row mx-n2"
|
||||||
|
>
|
||||||
|
<FormularioField
|
||||||
|
v-slot="{ context }"
|
||||||
|
name="name"
|
||||||
|
class="col col-auto px-2 mb-3"
|
||||||
|
>
|
||||||
|
<label for="customer-name">Name</label>
|
||||||
|
<input
|
||||||
|
id="customer-name"
|
||||||
|
v-model="context.model"
|
||||||
|
class="field form-control"
|
||||||
|
type="text"
|
||||||
|
@blur="context.runValidation"
|
||||||
|
>
|
||||||
|
</FormularioField>
|
||||||
|
</FormularioFieldGroup>
|
||||||
|
|
||||||
|
<h3>Address list</h3>
|
||||||
|
|
||||||
<FormularioField
|
<FormularioField
|
||||||
v-slot="addressList"
|
v-slot="addressList"
|
||||||
@ -19,7 +43,7 @@
|
|||||||
name="street"
|
name="street"
|
||||||
validation="required"
|
validation="required"
|
||||||
>
|
>
|
||||||
<label for="address-street">Street <span class="text-danger">*</span></label>
|
<label for="address-street">Street</label>
|
||||||
<input
|
<input
|
||||||
id="address-street"
|
id="address-street"
|
||||||
v-model="context.model"
|
v-model="context.model"
|
||||||
@ -43,7 +67,7 @@
|
|||||||
name="building"
|
name="building"
|
||||||
validation="^required|alphanumeric"
|
validation="^required|alphanumeric"
|
||||||
>
|
>
|
||||||
<label for="address-building">Building <span class="text-danger">*</span></label>
|
<label for="address-building">Building</label>
|
||||||
<input
|
<input
|
||||||
id="address-building"
|
id="address-building"
|
||||||
v-model="context.model"
|
v-model="context.model"
|
||||||
|
@ -3,7 +3,7 @@ import './bootstrap.scss'
|
|||||||
import { storiesOf } from '@storybook/vue'
|
import { storiesOf } from '@storybook/vue'
|
||||||
|
|
||||||
import Vue from 'vue'
|
import Vue from 'vue'
|
||||||
import VueFormulario from '../../dist/formulario.esm'
|
import VueFormulario from '@/index.ts'
|
||||||
|
|
||||||
import ExampleAddressList from './ExampleAddressList.tale'
|
import ExampleAddressList from './ExampleAddressList.tale'
|
||||||
|
|
||||||
|
@ -165,6 +165,7 @@ describe('FormularioForm', () => {
|
|||||||
const state = {
|
const state = {
|
||||||
address: {
|
address: {
|
||||||
street: null,
|
street: null,
|
||||||
|
building: null,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -175,7 +176,7 @@ describe('FormularioForm', () => {
|
|||||||
args: [],
|
args: [],
|
||||||
context: {
|
context: {
|
||||||
name: 'address.street',
|
name: 'address.street',
|
||||||
value: null,
|
value: '',
|
||||||
formValues: state,
|
formValues: state,
|
||||||
},
|
},
|
||||||
}],
|
}],
|
||||||
@ -211,6 +212,7 @@ describe('FormularioForm', () => {
|
|||||||
const state = {
|
const state = {
|
||||||
address: {
|
address: {
|
||||||
street: null,
|
street: null,
|
||||||
|
building: null,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -221,7 +223,7 @@ describe('FormularioForm', () => {
|
|||||||
args: [],
|
args: [],
|
||||||
context: {
|
context: {
|
||||||
name: 'address.street',
|
name: 'address.street',
|
||||||
value: null,
|
value: '',
|
||||||
formValues: state,
|
formValues: state,
|
||||||
},
|
},
|
||||||
}],
|
}],
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import { get, unset } from '@/utils/access'
|
import { get, set, unset } from '@/utils/access'
|
||||||
|
|
||||||
class Sample {
|
class Sample {
|
||||||
constructor() {
|
constructor() {
|
||||||
@ -25,8 +25,35 @@ describe('access', () => {
|
|||||||
[[{ c: 1 }, 2, 3], '[0].c', 1],
|
[[{ c: 1 }, 2, 3], '[0].c', 1],
|
||||||
[[{ c: 2 }, 2, 3], '[0].c', 2],
|
[[{ c: 2 }, 2, 3], '[0].c', 2],
|
||||||
[new Sample(), 'fieldA', 'fieldA'],
|
[new Sample(), 'fieldA', 'fieldA'],
|
||||||
])('gets by path', (record, path, expected) => {
|
])('gets by path', (state, path, expected) => {
|
||||||
expect(get(record, path)).toEqual(expected)
|
expect(get(state, path)).toEqual(expected)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('set', () => {
|
||||||
|
test.each([
|
||||||
|
[{}, 'a', 1, { a: 1 }],
|
||||||
|
[null, 'a', 1, { a: 1 }],
|
||||||
|
['', 'a', 1, { a: 1 }],
|
||||||
|
['lorem', 'a', 1, { a: 1 }],
|
||||||
|
[true, 'a', 1, { a: 1 }],
|
||||||
|
[{}, 'a.b', 1, { a: { b: 1 } }],
|
||||||
|
[{ a: { b: null } }, 'a.b', 1, { a: { b: 1 } }],
|
||||||
|
[{ a: false }, 'a.b', 1, { a: { b: 1 } }],
|
||||||
|
[{}, 'a[0]', 1, { a: [1] }],
|
||||||
|
[{ a: false }, 'a[0]', 1, { a: [1] }],
|
||||||
|
[{}, 'a[0].b', 1, { a: [{ b: 1 }] }],
|
||||||
|
[{ a: false }, 'a[0].b', 1, { a: [{ b: 1 }] }],
|
||||||
|
[{}, 'a[0].b.c', 1, { a: [{ b: { c: 1 } }] }],
|
||||||
|
[{}, 'a[0].b[0].c', 1, { a: [{ b: [{ c: 1 }] }] }],
|
||||||
|
[{ a: false }, 'a[0].b[0].c', 1, { a: [{ b: [{ c: 1 }] }] }],
|
||||||
|
[{ a: [{ b: false }] }, 'a[0].b[0].c', 1, { a: [{ b: [{ c: 1 }] }] }],
|
||||||
|
[{ a: { b: false } }, 'a[0].b[0].c', 1, { a: { 0: { b: [{ c: 1 }] }, b: false } }],
|
||||||
|
])('sets by path', (state, path, value, expected) => {
|
||||||
|
const processed = set(state, path, value)
|
||||||
|
|
||||||
|
expect(processed).toEqual(expected)
|
||||||
|
expect(processed === state).toBeFalsy()
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
@ -43,11 +70,11 @@ describe('access', () => {
|
|||||||
[{ a: { b: [{ c: 1 }, 2, 3] } }, 'a.b[0].c', { a: { b: [{}, 2, 3] } }],
|
[{ a: { b: [{ c: 1 }, 2, 3] } }, 'a.b[0].c', { a: { b: [{}, 2, 3] } }],
|
||||||
[{ a: { b: [{ c: 1 }, 2, 3] } }, 'a.b[1].c', { a: { b: [{ c: 1 }, 2, 3] } }],
|
[{ a: { b: [{ c: 1 }, 2, 3] } }, 'a.b[1].c', { a: { b: [{ c: 1 }, 2, 3] } }],
|
||||||
[[{ c: 1 }, 2, 3], '[0].c', [{}, 2, 3]],
|
[[{ c: 1 }, 2, 3], '[0].c', [{}, 2, 3]],
|
||||||
])('unsets by path', (record, path, expected) => {
|
])('unsets by path', (state, path, expected) => {
|
||||||
const processed = unset(record, path)
|
const processed = unset(state, path)
|
||||||
|
|
||||||
expect(processed).toEqual(expected)
|
expect(processed).toEqual(expected)
|
||||||
expect(processed === record).toBeFalsy()
|
expect(processed === state).toBeFalsy()
|
||||||
})
|
})
|
||||||
|
|
||||||
test.each`
|
test.each`
|
||||||
|
Loading…
Reference in New Issue
Block a user