Add spec tests for field helpers

pull/10616/head
Chris Goller 2017-10-11 17:03:30 -05:00 committed by Andrew Watkins
parent 4e8e9b6cb2
commit 4797361622
2 changed files with 94 additions and 16 deletions

View File

@ -1,10 +1,77 @@
import {numFunctions} from 'shared/reducers/helpers/field'
import _ from 'lodash'
import {
fieldWalk,
removeField,
getFieldsDeep,
fieldNamesDeep,
} from 'shared/reducers/helpers/fields'
describe('Formatting helpers', () => {
describe('formatBytes', () => {
it('returns null when passed a falsey value', () => {
const actual = numFunctions(null)
expect(actual).to.equal(0)
describe('field helpers', () => {
it('can walk all fields and get all names', () => {
const fields = [
{
name: 'fn1',
type: 'func',
args: [{name: 'f1', type: 'func', args: [{name: 'f2', type: 'field'}]}],
},
{name: 'fn1', type: 'func', args: [{name: 'f2', type: 'field'}]},
{name: 'fn2', type: 'func', args: [{name: 'f2', type: 'field'}]},
]
const actual = fieldWalk(fields, f => _.get(f, 'name'))
expect(actual).to.deep.equal(['fn1', 'f1', 'f2', 'fn1', 'f2', 'fn2', 'f2'])
})
it('can return all unique fields for type field', () => {
const fields = [
{
name: 'fn1',
type: 'func',
args: [{name: 'f1', type: 'func', args: [{name: 'f2', type: 'field'}]}],
},
{name: 'fn1', type: 'func', args: [{name: 'f2', type: 'field'}]},
{name: 'fn2', type: 'func', args: [{name: 'f2', type: 'field'}]},
]
const actual = getFieldsDeep(fields)
expect(actual).to.deep.equal([{name: 'f2', type: 'field'}])
})
it('can return all unique field names for type field', () => {
const fields = [
{
name: 'fn1',
type: 'func',
args: [{name: 'f1', type: 'func', args: [{name: 'f2', type: 'field'}]}],
},
{name: 'fn1', type: 'func', args: [{name: 'f2', type: 'field'}]},
{name: 'fn2', type: 'func', args: [{name: 'f2', type: 'field'}]},
]
const actual = fieldNamesDeep(fields)
expect(actual).to.deep.equal(['f2'])
})
describe('removeField', () => {
it('can remove fields at any level of the tree', () => {
const fields = [
{
name: 'fn1',
type: 'func',
args: [
{name: 'f1', type: 'func', args: [{name: 'f2', type: 'field'}]},
],
},
{name: 'fn2', type: 'func', args: [{name: 'f2', type: 'field'}]},
{name: 'fn3', type: 'func', args: [{name: 'f3', type: 'field'}]},
]
const actual = removeField('f2', fields)
expect(actual).to.deep.equal([
{name: 'fn3', type: 'func', args: [{name: 'f3', type: 'field'}]},
])
})
it('can remove fields from a flat field list', () => {
const fields = [{name: 'f1', type: 'field'}, {name: 'f2', type: 'field'}]
const actual = removeField('f2', fields)
expect(actual).to.deep.equal([{name: 'f1', type: 'field'}])
})
})
})

View File

@ -1,8 +1,15 @@
import _ from 'lodash'
// fieldWalk traverses fields rescursively into args
export const fieldWalk = (fields, fn) =>
fields.each(f => _.concat(fn(f), fieldWalk(_.get(f, 'args', []), fn)))
// fieldWalk traverses fields rescursively into args mapping fn on every
// field
export const fieldWalk = (fields, fn, acc = []) =>
_.compact(
_.flattenDeep(
fields.reduce((a, f) => {
return [...a, fn(f), fieldWalk(_.get(f, 'args', []), fn, acc)]
}, acc)
)
)
// functions returns all top-level fields with type
export const ofType = (fields, type) =>
@ -20,11 +27,14 @@ export const functionNames = fields => functions(fields).map(f => f.name)
// getFields returns all of the top-level fields of type field
export const getFields = fields => ofType(fields, 'field')
export const fieldsDeep = fields =>
_.uniqBy(fieldWalk(fields, f => getFields(f)), 'name')
export const getFieldsDeep = fields =>
_.uniqBy(
fieldWalk(fields, f => (_.get(f, 'type') === 'field' ? f : null)),
'name'
)
export const fieldNamesDeep = fields =>
fieldsDeep(fields).map(f => _.get(f, 'name'))
getFieldsDeep(fields).map(f => _.get(f, 'name'))
// firstFieldName returns the name of the first of type field
export const firstFieldName = fields => _.head(fieldNamesDeep(fields))
@ -43,17 +53,18 @@ export const everyField = fields => everyOfType(fields, 'field')
export const everyFunction = fields => everyOfType(fields, 'func')
// removeField will remove the field or function from the field list with the
// given fieldName
// given fieldName. Preconditions: only type field OR only type func
export const removeField = (fieldName, fields) => {
if (everyField(fields)) {
return fields.filter(f => f.name !== fieldName)
}
return fields.reduce((acc, f) => {
const has = fieldNamesDeep(f.args).some(n => n.name === fieldName)
const has = fieldNamesDeep(f.args).some(n => n === fieldName)
if (has) {
return [...acc, f]
return acc
}
return acc
return [...acc, f]
}, [])
}