Implemented support for selection in eson
This commit is contained in:
parent
f3313158db
commit
943d721d84
|
@ -52,7 +52,7 @@ export default class JSONNode extends Component {
|
||||||
if (data.expanded) {
|
if (data.expanded) {
|
||||||
if (data.props.length > 0) {
|
if (data.props.length > 0) {
|
||||||
const props = data.props.map(prop => {
|
const props = data.props.map(prop => {
|
||||||
return h('li', {key: prop.id},
|
return h('li', { key: prop.id, className: (prop.value.selected ? ' jsoneditor-selected' : '') },
|
||||||
h(this.constructor, {
|
h(this.constructor, {
|
||||||
path: this.props.path.concat(prop.name),
|
path: this.props.path.concat(prop.name),
|
||||||
prop,
|
prop,
|
||||||
|
@ -98,7 +98,7 @@ export default class JSONNode extends Component {
|
||||||
if (data.expanded) {
|
if (data.expanded) {
|
||||||
if (data.items.length > 0) {
|
if (data.items.length > 0) {
|
||||||
const items = data.items.map((item, index) => {
|
const items = data.items.map((item, index) => {
|
||||||
return h('li', {key : item.id},
|
return h('li', { key : item.id, className: (item.value.selected ? ' jsoneditor-selected' : '')},
|
||||||
h(this.constructor, {
|
h(this.constructor, {
|
||||||
path: this.props.path.concat(String(index)),
|
path: this.props.path.concat(String(index)),
|
||||||
index,
|
index,
|
||||||
|
|
|
@ -11,7 +11,8 @@ import { enrichSchemaError } from '../utils/schemaUtils'
|
||||||
import {
|
import {
|
||||||
jsonToEson, esonToJson, toEsonPath, pathExists,
|
jsonToEson, esonToJson, toEsonPath, pathExists,
|
||||||
expand, expandPath, addErrors,
|
expand, expandPath, addErrors,
|
||||||
search, addSearchResults, nextSearchResult, previousSearchResult,
|
search, applySearchResults, nextSearchResult, previousSearchResult,
|
||||||
|
applySelection,
|
||||||
compileJSONPointer
|
compileJSONPointer
|
||||||
} from '../eson'
|
} from '../eson'
|
||||||
import { patchEson } from '../patchEson'
|
import { patchEson } from '../patchEson'
|
||||||
|
@ -93,6 +94,11 @@ export default class TreeMode extends Component {
|
||||||
search: {
|
search: {
|
||||||
text: '',
|
text: '',
|
||||||
active: null // active search result
|
active: null // active search result
|
||||||
|
},
|
||||||
|
|
||||||
|
selection: {
|
||||||
|
start: null, // ESONPointer
|
||||||
|
end: null, // ESONPointer
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -162,7 +168,10 @@ export default class TreeMode extends Component {
|
||||||
// TODO: performance improvements in search would be nice though it's acceptable right now
|
// TODO: performance improvements in search would be nice though it's acceptable right now
|
||||||
const searchResults = this.state.search.text ? search(data, this.state.search.text) : null
|
const searchResults = this.state.search.text ? search(data, this.state.search.text) : null
|
||||||
if (searchResults) {
|
if (searchResults) {
|
||||||
data = addSearchResults(data, searchResults, this.state.search.active)
|
data = applySearchResults(data, searchResults, this.state.search.active)
|
||||||
|
}
|
||||||
|
if (this.state.selection) {
|
||||||
|
data = applySelection(data, this.state.selection)
|
||||||
}
|
}
|
||||||
|
|
||||||
return h('div', {
|
return h('div', {
|
||||||
|
@ -178,7 +187,7 @@ export default class TreeMode extends Component {
|
||||||
className: 'jsoneditor-contents jsoneditor-tree-contents',
|
className: 'jsoneditor-contents jsoneditor-tree-contents',
|
||||||
id: this.id
|
id: this.id
|
||||||
},
|
},
|
||||||
h('ul', {className: 'jsoneditor-list jsoneditor-root'},
|
h('ul', {className: 'jsoneditor-list jsoneditor-root' + (data.selected ? ' jsoneditor-selected' : '')},
|
||||||
h(Node, {
|
h(Node, {
|
||||||
data,
|
data,
|
||||||
events: state.events,
|
events: state.events,
|
||||||
|
|
88
src/eson.js
88
src/eson.js
|
@ -5,13 +5,13 @@
|
||||||
* All functions are pure and don't mutate the ESON.
|
* All functions are pure and don't mutate the ESON.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { setIn, getIn } from './utils/immutabilityHelpers'
|
import { setIn, getIn, updateIn } from './utils/immutabilityHelpers'
|
||||||
import { isObject } from './utils/typeUtils'
|
import { isObject } from './utils/typeUtils'
|
||||||
import { last, allButLast } from './utils/arrayUtils'
|
import { last, allButLast } from './utils/arrayUtils'
|
||||||
import isEqual from 'lodash/isEqual'
|
import isEqual from 'lodash/isEqual'
|
||||||
|
|
||||||
import type {
|
import type {
|
||||||
ESON, ESONObject, ESONArrayItem, ESONPointer, ESONType, ESONPath,
|
ESON, ESONObject, ESONArrayItem, ESONPointer, ESONSelection, ESONType, ESONPath,
|
||||||
Path,
|
Path,
|
||||||
JSONPath, JSONType
|
JSONPath, JSONType
|
||||||
} from './types'
|
} from './types'
|
||||||
|
@ -222,7 +222,7 @@ export function search (eson: ESON, text: string): ESONPointer[] {
|
||||||
const parentPath = allButLast(path)
|
const parentPath = allButLast(path)
|
||||||
const parent = getIn(eson, toEsonPath(eson, parentPath))
|
const parent = getIn(eson, toEsonPath(eson, parentPath))
|
||||||
if (parent.type === 'Object') {
|
if (parent.type === 'Object') {
|
||||||
results.push({path, type: 'property'})
|
results.push({path, field: 'property'})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -230,7 +230,7 @@ export function search (eson: ESON, text: string): ESONPointer[] {
|
||||||
// check value
|
// check value
|
||||||
if (value.type === 'value') {
|
if (value.type === 'value') {
|
||||||
if (containsCaseInsensitive(value.value, text)) {
|
if (containsCaseInsensitive(value.value, text)) {
|
||||||
results.push({path, type: 'value'})
|
results.push({path, field: 'value'})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
@ -290,17 +290,17 @@ export function previousSearchResult (searchResults: ESONPointer[], current: ESO
|
||||||
/**
|
/**
|
||||||
* Merge searchResults into the eson object
|
* Merge searchResults into the eson object
|
||||||
*/
|
*/
|
||||||
export function addSearchResults (eson: ESON, searchResults: ESONPointer[], activeSearchResult: ESONPointer) {
|
export function applySearchResults (eson: ESON, searchResults: ESONPointer[], activeSearchResult: ESONPointer) {
|
||||||
let updatedEson = eson
|
let updatedEson = eson
|
||||||
|
|
||||||
searchResults.forEach(function (searchResult) {
|
searchResults.forEach(function (searchResult) {
|
||||||
if (searchResult.type === 'value') {
|
if (searchResult.field === 'value') {
|
||||||
const esonPath = toEsonPath(updatedEson, searchResult.path).concat('searchResult')
|
const esonPath = toEsonPath(updatedEson, searchResult.path).concat('searchResult')
|
||||||
const value = isEqual(searchResult, activeSearchResult) ? 'active' : 'normal'
|
const value = isEqual(searchResult, activeSearchResult) ? 'active' : 'normal'
|
||||||
updatedEson = setIn(updatedEson, esonPath, value)
|
updatedEson = setIn(updatedEson, esonPath, value)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (searchResult.type === 'property') {
|
if (searchResult.field === 'property') {
|
||||||
const esonPath = toEsonPath(updatedEson, searchResult.path)
|
const esonPath = toEsonPath(updatedEson, searchResult.path)
|
||||||
const propertyPath = allButLast(esonPath).concat('searchResult')
|
const propertyPath = allButLast(esonPath).concat('searchResult')
|
||||||
const value = isEqual(searchResult, activeSearchResult) ? 'active' : 'normal'
|
const value = isEqual(searchResult, activeSearchResult) ? 'active' : 'normal'
|
||||||
|
@ -312,13 +312,65 @@ export function addSearchResults (eson: ESON, searchResults: ESONPointer[], acti
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Do a case insensitive search for a search text in a text
|
* Merge searchResults into the eson object
|
||||||
* @param {String} text
|
|
||||||
* @param {String} search
|
|
||||||
* @return {boolean} Returns true if `search` is found in `text`
|
|
||||||
*/
|
*/
|
||||||
export function containsCaseInsensitive (text: string, search: string): boolean {
|
export function applySelection (eson: ESON, selection: ESONSelection) {
|
||||||
return String(text).toLowerCase().indexOf(search.toLowerCase()) !== -1
|
if (!selection || !selection.start || !selection.end) {
|
||||||
|
return eson
|
||||||
|
}
|
||||||
|
|
||||||
|
// find the parent node shared by both start and end of the selection
|
||||||
|
const rootPath = findSharedPath(selection.start.path, selection.end.path)
|
||||||
|
const rootEsonPath = toEsonPath(eson, rootPath)
|
||||||
|
|
||||||
|
if (rootPath.length === selection.start.path.length || rootPath.length === selection.end.path.length) {
|
||||||
|
// select the root itself
|
||||||
|
return setIn(eson, rootEsonPath.concat(['selected']), true)
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// select multiple childs of an object or array
|
||||||
|
return updateIn(eson, rootEsonPath, (root) => {
|
||||||
|
if (root.type === 'Object') {
|
||||||
|
const startIndex = findPropertyIndex(root, selection.start.path[rootPath.length])
|
||||||
|
const endIndex = findPropertyIndex(root, selection.end.path[rootPath.length])
|
||||||
|
const minIndex = Math.min(startIndex, endIndex)
|
||||||
|
const maxIndex = Math.max(startIndex, endIndex) + 1 // include max index itself
|
||||||
|
|
||||||
|
const propsBefore = root.props.slice(0, minIndex)
|
||||||
|
const propsUpdated = root.props.slice(minIndex, maxIndex)
|
||||||
|
.map((prop, index) => setIn(prop, ['value', 'selected'], true))
|
||||||
|
const propsAfter = root.props.slice(maxIndex)
|
||||||
|
|
||||||
|
return setIn(root, ['props'], propsBefore.concat(propsUpdated, propsAfter))
|
||||||
|
}
|
||||||
|
else if (root.type === 'Array') {
|
||||||
|
const startIndex = parseInt(selection.start.path[rootPath.length])
|
||||||
|
const endIndex = parseInt(selection.end.path[rootPath.length])
|
||||||
|
const minIndex = Math.min(startIndex, endIndex)
|
||||||
|
const maxIndex = Math.max(startIndex, endIndex) + 1 // include max index itself
|
||||||
|
|
||||||
|
const itemsBefore = root.items.slice(0, minIndex)
|
||||||
|
const itemsUpdated = root.items.slice(minIndex, maxIndex)
|
||||||
|
.map((item, index) => setIn(item, ['value', 'selected'], true))
|
||||||
|
const itemsAfter = root.items.slice(maxIndex)
|
||||||
|
|
||||||
|
return setIn(root, ['items'], itemsBefore.concat(itemsUpdated, itemsAfter))
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Find the common path of two paths.
|
||||||
|
* For example findCommonRoot(['arr', '1', 'name'], ['arr', '1', 'address', 'contact']) returns ['arr', '1']
|
||||||
|
*/
|
||||||
|
function findSharedPath (path1: JSONPath, path2: JSONPath): JSONPath {
|
||||||
|
let i = 0;
|
||||||
|
while (i < path1.length && path1[i] === path2[i]) {
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
|
||||||
|
return path1.slice(0, i)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -520,6 +572,16 @@ export function compileJSONPointer (path: Path) {
|
||||||
|
|
||||||
// TODO: move getId and createUniqueId to a separate file
|
// TODO: move getId and createUniqueId to a separate file
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Do a case insensitive search for a search text in a text
|
||||||
|
* @param {String} text
|
||||||
|
* @param {String} search
|
||||||
|
* @return {boolean} Returns true if `search` is found in `text`
|
||||||
|
*/
|
||||||
|
export function containsCaseInsensitive (text: string, search: string): boolean {
|
||||||
|
return String(text).toLowerCase().indexOf(search.toLowerCase()) !== -1
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get a new "unique" id. Id's are created from an incremental counter.
|
* Get a new "unique" id. Id's are created from an incremental counter.
|
||||||
* @return {number}
|
* @return {number}
|
||||||
|
|
|
@ -256,6 +256,10 @@ div.jsoneditor-value.jsoneditor-empty::after {
|
||||||
content: 'value';
|
content: 'value';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.jsoneditor-selected {
|
||||||
|
background-color: #f0f0f0;
|
||||||
|
}
|
||||||
|
|
||||||
.jsoneditor-highlight {
|
.jsoneditor-highlight {
|
||||||
background-color: yellow;
|
background-color: yellow;
|
||||||
}
|
}
|
||||||
|
|
14
src/types.js
14
src/types.js
|
@ -33,7 +33,7 @@ export type JSONArrayType = JSONType[]
|
||||||
/********************** TYPES FOR THE ESON OBJECT MODEL *************************/
|
/********************** TYPES FOR THE ESON OBJECT MODEL *************************/
|
||||||
|
|
||||||
export type SearchResultStatus = 'normal' | 'active'
|
export type SearchResultStatus = 'normal' | 'active'
|
||||||
export type ESONPointerType = 'value' | 'property'
|
export type ESONPointerField = 'value' | 'property'
|
||||||
|
|
||||||
export type ESONObjectProperty = {
|
export type ESONObjectProperty = {
|
||||||
id: number,
|
id: number,
|
||||||
|
@ -50,18 +50,21 @@ export type ESONArrayItem = {
|
||||||
export type ESONObject = {
|
export type ESONObject = {
|
||||||
type: 'Object',
|
type: 'Object',
|
||||||
expanded?: boolean,
|
expanded?: boolean,
|
||||||
|
selected?: boolean,
|
||||||
props: ESONObjectProperty[]
|
props: ESONObjectProperty[]
|
||||||
}
|
}
|
||||||
|
|
||||||
export type ESONArray = {
|
export type ESONArray = {
|
||||||
type: 'Array',
|
type: 'Array',
|
||||||
expanded?: boolean,
|
expanded?: boolean,
|
||||||
|
selected?: boolean,
|
||||||
items: ESONArrayItem[]
|
items: ESONArrayItem[]
|
||||||
}
|
}
|
||||||
|
|
||||||
export type ESONValue = {
|
export type ESONValue = {
|
||||||
type: 'value' | 'string',
|
type: 'value' | 'string',
|
||||||
value?: any,
|
value?: any,
|
||||||
|
selected?: boolean,
|
||||||
searchResult?: SearchResultStatus
|
searchResult?: SearchResultStatus
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -74,8 +77,13 @@ export type JSONPath = string[]
|
||||||
export type ESONPath = string[]
|
export type ESONPath = string[]
|
||||||
|
|
||||||
export type ESONPointer = {
|
export type ESONPointer = {
|
||||||
path: ESONPath,
|
path: JSONPath, // TODO: change path to an ESONPath?
|
||||||
type: ESONPointerType
|
field?: ESONPointerField
|
||||||
|
}
|
||||||
|
|
||||||
|
export type ESONSelection = {
|
||||||
|
start: ESONPointer,
|
||||||
|
end: ESONPointer
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: ESONPointer.path is an array, JSONSchemaError.path is a string -> make this consistent
|
// TODO: ESONPointer.path is an array, JSONSchemaError.path is a string -> make this consistent
|
||||||
|
|
|
@ -2,10 +2,13 @@ import test from 'ava';
|
||||||
import {
|
import {
|
||||||
jsonToEson, esonToJson, pathExists, transform, traverse,
|
jsonToEson, esonToJson, pathExists, transform, traverse,
|
||||||
parseJSONPointer, compileJSONPointer,
|
parseJSONPointer, compileJSONPointer,
|
||||||
expand, addErrors, search, addSearchResults, nextSearchResult, previousSearchResult
|
expand, addErrors, search, applySearchResults, nextSearchResult, previousSearchResult,
|
||||||
|
applySelection
|
||||||
} from '../src/eson'
|
} from '../src/eson'
|
||||||
|
|
||||||
|
|
||||||
|
// TODO: move all JSON documents in separate json files to keep the test readable?
|
||||||
|
|
||||||
const JSON_EXAMPLE = {
|
const JSON_EXAMPLE = {
|
||||||
obj: {
|
obj: {
|
||||||
arr: [1,2, {first:3,last:4}]
|
arr: [1,2, {first:3,last:4}]
|
||||||
|
@ -15,7 +18,7 @@ const JSON_EXAMPLE = {
|
||||||
bool: false
|
bool: false
|
||||||
}
|
}
|
||||||
|
|
||||||
const JSON_DATA_EXAMPLE = {
|
const ESON = {
|
||||||
type: 'Object',
|
type: 'Object',
|
||||||
expanded: true,
|
expanded: true,
|
||||||
props: [
|
props: [
|
||||||
|
@ -105,34 +108,9 @@ const JSON_DATA_EXAMPLE = {
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
const JSON_DUPLICATE_PROPERTY_EXAMPLE = {
|
// TODO: instead of all slightly different copies of ESON, built them up via setIn, updateIn based on ESON
|
||||||
type: 'Object',
|
|
||||||
expanded: true,
|
|
||||||
props: [
|
|
||||||
{
|
|
||||||
id: '[ID]',
|
|
||||||
name: 'name',
|
|
||||||
value: {
|
|
||||||
type: 'value',
|
|
||||||
expanded: true,
|
|
||||||
value: 'Joe'
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: '[ID]',
|
|
||||||
name: 'name',
|
|
||||||
value: {
|
|
||||||
type: 'value',
|
|
||||||
expanded: true,
|
|
||||||
value: 'Joe'
|
|
||||||
}
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: instead of all slightly different copies of JSON_DATA_EXAMPLE, built them up via setIn, updateIn based on JSON_DATA_EXAMPLE
|
const ESON_COLLAPSED_1 = {
|
||||||
|
|
||||||
const JSON_DATA_EXAMPLE_COLLAPSED_1 = {
|
|
||||||
type: 'Object',
|
type: 'Object',
|
||||||
expanded: true,
|
expanded: true,
|
||||||
props: [
|
props: [
|
||||||
|
@ -223,7 +201,7 @@ const JSON_DATA_EXAMPLE_COLLAPSED_1 = {
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
const JSON_DATA_EXAMPLE_COLLAPSED_2 = {
|
const ESON_COLLAPSED_2 = {
|
||||||
type: 'Object',
|
type: 'Object',
|
||||||
expanded: true,
|
expanded: true,
|
||||||
props: [
|
props: [
|
||||||
|
@ -314,7 +292,7 @@ const JSON_DATA_EXAMPLE_COLLAPSED_2 = {
|
||||||
}
|
}
|
||||||
|
|
||||||
// after search for 'L' (case insensitive)
|
// after search for 'L' (case insensitive)
|
||||||
const JSON_DATA_EXAMPLE_SEARCH_L = {
|
const ESON_SEARCH_L = {
|
||||||
type: 'Object',
|
type: 'Object',
|
||||||
expanded: true,
|
expanded: true,
|
||||||
props: [
|
props: [
|
||||||
|
@ -410,7 +388,374 @@ const JSON_DATA_EXAMPLE_SEARCH_L = {
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
const JSON_DATA_SMALL = {
|
const ESON_SELECTED_OBJECT = {
|
||||||
|
type: 'Object',
|
||||||
|
expanded: true,
|
||||||
|
props: [
|
||||||
|
{
|
||||||
|
id: '[ID]',
|
||||||
|
name: 'obj',
|
||||||
|
value: {
|
||||||
|
type: 'Object',
|
||||||
|
expanded: true,
|
||||||
|
selected: true,
|
||||||
|
props: [
|
||||||
|
{
|
||||||
|
id: '[ID]',
|
||||||
|
name: 'arr',
|
||||||
|
value: {
|
||||||
|
type: 'Array',
|
||||||
|
expanded: true,
|
||||||
|
items: [
|
||||||
|
{
|
||||||
|
id: '[ID]',
|
||||||
|
value: {
|
||||||
|
type: 'value',
|
||||||
|
value: 1
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: '[ID]',
|
||||||
|
value: {
|
||||||
|
type: 'value',
|
||||||
|
value: 2
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: '[ID]',
|
||||||
|
value: {
|
||||||
|
type: 'Object',
|
||||||
|
expanded: true,
|
||||||
|
props: [
|
||||||
|
{
|
||||||
|
id: '[ID]',
|
||||||
|
name: 'first',
|
||||||
|
value: {
|
||||||
|
type: 'value',
|
||||||
|
value: 3
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: '[ID]',
|
||||||
|
name: 'last',
|
||||||
|
value: {
|
||||||
|
type: 'value',
|
||||||
|
value: 4
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: '[ID]',
|
||||||
|
name: 'str',
|
||||||
|
value: {
|
||||||
|
type: 'value',
|
||||||
|
value: 'hello world',
|
||||||
|
selected: true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: '[ID]',
|
||||||
|
name: 'nill',
|
||||||
|
value: {
|
||||||
|
type: 'value',
|
||||||
|
value: null,
|
||||||
|
selected: true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: '[ID]',
|
||||||
|
name: 'bool',
|
||||||
|
value: {
|
||||||
|
type: 'value',
|
||||||
|
value: false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
const ESON_SELECTED_ARRAY = {
|
||||||
|
type: 'Object',
|
||||||
|
expanded: true,
|
||||||
|
props: [
|
||||||
|
{
|
||||||
|
id: '[ID]',
|
||||||
|
name: 'obj',
|
||||||
|
value: {
|
||||||
|
type: 'Object',
|
||||||
|
expanded: true,
|
||||||
|
props: [
|
||||||
|
{
|
||||||
|
id: '[ID]',
|
||||||
|
name: 'arr',
|
||||||
|
value: {
|
||||||
|
type: 'Array',
|
||||||
|
expanded: true,
|
||||||
|
items: [
|
||||||
|
{
|
||||||
|
id: '[ID]',
|
||||||
|
value: {
|
||||||
|
type: 'value',
|
||||||
|
selected: true,
|
||||||
|
value: 1
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: '[ID]',
|
||||||
|
value: {
|
||||||
|
type: 'value',
|
||||||
|
selected: true,
|
||||||
|
value: 2
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: '[ID]',
|
||||||
|
value: {
|
||||||
|
type: 'Object',
|
||||||
|
expanded: true,
|
||||||
|
props: [
|
||||||
|
{
|
||||||
|
id: '[ID]',
|
||||||
|
name: 'first',
|
||||||
|
value: {
|
||||||
|
type: 'value',
|
||||||
|
value: 3
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: '[ID]',
|
||||||
|
name: 'last',
|
||||||
|
value: {
|
||||||
|
type: 'value',
|
||||||
|
value: 4
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: '[ID]',
|
||||||
|
name: 'str',
|
||||||
|
value: {
|
||||||
|
type: 'value',
|
||||||
|
value: 'hello world'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: '[ID]',
|
||||||
|
name: 'nill',
|
||||||
|
value: {
|
||||||
|
type: 'value',
|
||||||
|
value: null
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: '[ID]',
|
||||||
|
name: 'bool',
|
||||||
|
value: {
|
||||||
|
type: 'value',
|
||||||
|
value: false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
const ESON_SELECTED_VALUE = {
|
||||||
|
type: 'Object',
|
||||||
|
expanded: true,
|
||||||
|
props: [
|
||||||
|
{
|
||||||
|
id: '[ID]',
|
||||||
|
name: 'obj',
|
||||||
|
value: {
|
||||||
|
type: 'Object',
|
||||||
|
expanded: true,
|
||||||
|
props: [
|
||||||
|
{
|
||||||
|
id: '[ID]',
|
||||||
|
name: 'arr',
|
||||||
|
value: {
|
||||||
|
type: 'Array',
|
||||||
|
expanded: true,
|
||||||
|
items: [
|
||||||
|
{
|
||||||
|
id: '[ID]',
|
||||||
|
value: {
|
||||||
|
type: 'value',
|
||||||
|
value: 1
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: '[ID]',
|
||||||
|
value: {
|
||||||
|
type: 'value',
|
||||||
|
value: 2
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: '[ID]',
|
||||||
|
value: {
|
||||||
|
type: 'Object',
|
||||||
|
expanded: true,
|
||||||
|
props: [
|
||||||
|
{
|
||||||
|
id: '[ID]',
|
||||||
|
name: 'first',
|
||||||
|
value: {
|
||||||
|
type: 'value',
|
||||||
|
selected: true,
|
||||||
|
value: 3
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: '[ID]',
|
||||||
|
name: 'last',
|
||||||
|
value: {
|
||||||
|
type: 'value',
|
||||||
|
value: 4
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: '[ID]',
|
||||||
|
name: 'str',
|
||||||
|
value: {
|
||||||
|
type: 'value',
|
||||||
|
value: 'hello world'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: '[ID]',
|
||||||
|
name: 'nill',
|
||||||
|
value: {
|
||||||
|
type: 'value',
|
||||||
|
value: null
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: '[ID]',
|
||||||
|
name: 'bool',
|
||||||
|
value: {
|
||||||
|
type: 'value',
|
||||||
|
value: false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
const ESON_SELECTED_PARENT = {
|
||||||
|
type: 'Object',
|
||||||
|
expanded: true,
|
||||||
|
props: [
|
||||||
|
{
|
||||||
|
id: '[ID]',
|
||||||
|
name: 'obj',
|
||||||
|
value: {
|
||||||
|
type: 'Object',
|
||||||
|
expanded: true,
|
||||||
|
props: [
|
||||||
|
{
|
||||||
|
id: '[ID]',
|
||||||
|
name: 'arr',
|
||||||
|
value: {
|
||||||
|
type: 'Array',
|
||||||
|
expanded: true,
|
||||||
|
items: [
|
||||||
|
{
|
||||||
|
id: '[ID]',
|
||||||
|
value: {
|
||||||
|
type: 'value',
|
||||||
|
value: 1
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: '[ID]',
|
||||||
|
value: {
|
||||||
|
type: 'value',
|
||||||
|
value: 2
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: '[ID]',
|
||||||
|
value: {
|
||||||
|
type: 'Object',
|
||||||
|
expanded: true,
|
||||||
|
selected: true,
|
||||||
|
props: [
|
||||||
|
{
|
||||||
|
id: '[ID]',
|
||||||
|
name: 'first',
|
||||||
|
value: {
|
||||||
|
type: 'value',
|
||||||
|
value: 3
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: '[ID]',
|
||||||
|
name: 'last',
|
||||||
|
value: {
|
||||||
|
type: 'value',
|
||||||
|
value: 4
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: '[ID]',
|
||||||
|
name: 'str',
|
||||||
|
value: {
|
||||||
|
type: 'value',
|
||||||
|
value: 'hello world'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: '[ID]',
|
||||||
|
name: 'nill',
|
||||||
|
value: {
|
||||||
|
type: 'value',
|
||||||
|
value: null
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: '[ID]',
|
||||||
|
name: 'bool',
|
||||||
|
value: {
|
||||||
|
type: 'value',
|
||||||
|
value: false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
const ESON_SMALL = {
|
||||||
type: 'Object',
|
type: 'Object',
|
||||||
props: [
|
props: [
|
||||||
{
|
{
|
||||||
|
@ -455,7 +800,7 @@ const JSON_SCHEMA_ERRORS = [
|
||||||
{dataPath: '/nill', message: 'Null expected'}
|
{dataPath: '/nill', message: 'Null expected'}
|
||||||
]
|
]
|
||||||
|
|
||||||
const JSON_DATA_EXAMPLE_ERRORS = {
|
const ESON_ERRORS = {
|
||||||
type: 'Object',
|
type: 'Object',
|
||||||
expanded: true,
|
expanded: true,
|
||||||
props: [
|
props: [
|
||||||
|
@ -555,17 +900,17 @@ test('jsonToEson', t => {
|
||||||
const ESON = jsonToEson(JSON_EXAMPLE, expand, [])
|
const ESON = jsonToEson(JSON_EXAMPLE, expand, [])
|
||||||
replaceIds(ESON)
|
replaceIds(ESON)
|
||||||
|
|
||||||
t.deepEqual(ESON, JSON_DATA_EXAMPLE)
|
t.deepEqual(ESON, ESON)
|
||||||
})
|
})
|
||||||
|
|
||||||
test('esonToJson', t => {
|
test('esonToJson', t => {
|
||||||
t.deepEqual(esonToJson(JSON_DATA_EXAMPLE), JSON_EXAMPLE)
|
t.deepEqual(esonToJson(ESON), JSON_EXAMPLE)
|
||||||
})
|
})
|
||||||
|
|
||||||
test('expand a single path', t => {
|
test('expand a single path', t => {
|
||||||
const collapsed = expand(JSON_DATA_EXAMPLE, ['obj', 'arr', 2], false)
|
const collapsed = expand(ESON, ['obj', 'arr', 2], false)
|
||||||
|
|
||||||
t.deepEqual(collapsed, JSON_DATA_EXAMPLE_COLLAPSED_1)
|
t.deepEqual(collapsed, ESON_COLLAPSED_1)
|
||||||
})
|
})
|
||||||
|
|
||||||
test('expand a callback', t => {
|
test('expand a callback', t => {
|
||||||
|
@ -573,9 +918,9 @@ test('expand a callback', t => {
|
||||||
return path.length >= 1
|
return path.length >= 1
|
||||||
}
|
}
|
||||||
const expanded = false
|
const expanded = false
|
||||||
const collapsed = expand(JSON_DATA_EXAMPLE, callback, expanded)
|
const collapsed = expand(ESON, callback, expanded)
|
||||||
|
|
||||||
t.deepEqual(collapsed, JSON_DATA_EXAMPLE_COLLAPSED_2)
|
t.deepEqual(collapsed, ESON_COLLAPSED_2)
|
||||||
})
|
})
|
||||||
|
|
||||||
test('expand a callback should not change the object when nothing happens', t => {
|
test('expand a callback should not change the object when nothing happens', t => {
|
||||||
|
@ -583,16 +928,16 @@ test('expand a callback should not change the object when nothing happens', t =>
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
const expanded = false
|
const expanded = false
|
||||||
const collapsed = expand(JSON_DATA_EXAMPLE, callback, expanded)
|
const collapsed = expand(ESON, callback, expanded)
|
||||||
|
|
||||||
t.is(collapsed, JSON_DATA_EXAMPLE)
|
t.is(collapsed, ESON)
|
||||||
})
|
})
|
||||||
|
|
||||||
test('pathExists', t => {
|
test('pathExists', t => {
|
||||||
t.is(pathExists(JSON_DATA_EXAMPLE, ['obj', 'arr', 2, 'first']), true)
|
t.is(pathExists(ESON, ['obj', 'arr', 2, 'first']), true)
|
||||||
t.is(pathExists(JSON_DATA_EXAMPLE, ['obj', 'foo']), false)
|
t.is(pathExists(ESON, ['obj', 'foo']), false)
|
||||||
t.is(pathExists(JSON_DATA_EXAMPLE, ['obj', 'foo', 'bar']), false)
|
t.is(pathExists(ESON, ['obj', 'foo', 'bar']), false)
|
||||||
t.is(pathExists(JSON_DATA_EXAMPLE, []), true)
|
t.is(pathExists(ESON, []), true)
|
||||||
})
|
})
|
||||||
|
|
||||||
test('parseJSONPointer', t => {
|
test('parseJSONPointer', t => {
|
||||||
|
@ -612,16 +957,16 @@ test('compileJSONPointer', t => {
|
||||||
})
|
})
|
||||||
|
|
||||||
test('add and remove errors', t => {
|
test('add and remove errors', t => {
|
||||||
const dataWithErrors = addErrors(JSON_DATA_EXAMPLE, JSON_SCHEMA_ERRORS)
|
const dataWithErrors = addErrors(ESON, JSON_SCHEMA_ERRORS)
|
||||||
t.deepEqual(dataWithErrors, JSON_DATA_EXAMPLE_ERRORS)
|
t.deepEqual(dataWithErrors, ESON_ERRORS)
|
||||||
})
|
})
|
||||||
|
|
||||||
test('transform', t => {
|
test('transform', t => {
|
||||||
// {obj: {a: 2}, arr: [3]}
|
// {obj: {a: 2}, arr: [3]}
|
||||||
|
|
||||||
let log = []
|
let log = []
|
||||||
const transformed = transform(JSON_DATA_SMALL, function (value, path, root) {
|
const transformed = transform(ESON_SMALL, function (value, path, root) {
|
||||||
t.is(root, JSON_DATA_SMALL)
|
t.is(root, ESON_SMALL)
|
||||||
|
|
||||||
log.push([value, path, root])
|
log.push([value, path, root])
|
||||||
|
|
||||||
|
@ -637,22 +982,22 @@ test('transform', t => {
|
||||||
// console.log('transformed', JSON.stringify(transformed, null, 2))
|
// console.log('transformed', JSON.stringify(transformed, null, 2))
|
||||||
|
|
||||||
const EXPECTED_LOG = [
|
const EXPECTED_LOG = [
|
||||||
[JSON_DATA_SMALL, [], JSON_DATA_SMALL],
|
[ESON_SMALL, [], ESON_SMALL],
|
||||||
[JSON_DATA_SMALL.props[0].value, ['obj'], JSON_DATA_SMALL],
|
[ESON_SMALL.props[0].value, ['obj'], ESON_SMALL],
|
||||||
[JSON_DATA_SMALL.props[0].value.props[0].value, ['obj', 'a'], JSON_DATA_SMALL],
|
[ESON_SMALL.props[0].value.props[0].value, ['obj', 'a'], ESON_SMALL],
|
||||||
[JSON_DATA_SMALL.props[1].value, ['arr'], JSON_DATA_SMALL],
|
[ESON_SMALL.props[1].value, ['arr'], ESON_SMALL],
|
||||||
[JSON_DATA_SMALL.props[1].value.items[0].value, ['arr', '0'], JSON_DATA_SMALL],
|
[ESON_SMALL.props[1].value.items[0].value, ['arr', '0'], ESON_SMALL],
|
||||||
]
|
]
|
||||||
|
|
||||||
log.forEach((row, index) => {
|
log.forEach((row, index) => {
|
||||||
t.deepEqual(log[index], EXPECTED_LOG[index], 'should have equal log at index ' + index )
|
t.deepEqual(log[index], EXPECTED_LOG[index], 'should have equal log at index ' + index )
|
||||||
})
|
})
|
||||||
t.deepEqual(log, EXPECTED_LOG)
|
t.deepEqual(log, EXPECTED_LOG)
|
||||||
t.not(transformed, JSON_DATA_SMALL)
|
t.not(transformed, ESON_SMALL)
|
||||||
t.not(transformed.props[0].value, JSON_DATA_SMALL.props[0].value)
|
t.not(transformed.props[0].value, ESON_SMALL.props[0].value)
|
||||||
t.not(transformed.props[0].value.props[0].value, JSON_DATA_SMALL.props[0].value.props[0].value)
|
t.not(transformed.props[0].value.props[0].value, ESON_SMALL.props[0].value.props[0].value)
|
||||||
t.is(transformed.props[1].value, JSON_DATA_SMALL.props[1].value)
|
t.is(transformed.props[1].value, ESON_SMALL.props[1].value)
|
||||||
t.is(transformed.props[1].value.items[0].value, JSON_DATA_SMALL.props[1].value.items[0].value)
|
t.is(transformed.props[1].value.items[0].value, ESON_SMALL.props[1].value.items[0].value)
|
||||||
|
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -660,8 +1005,8 @@ test('traverse', t => {
|
||||||
// {obj: {a: 2}, arr: [3]}
|
// {obj: {a: 2}, arr: [3]}
|
||||||
|
|
||||||
let log = []
|
let log = []
|
||||||
const returnValue = traverse(JSON_DATA_SMALL, function (value, path, root) {
|
const returnValue = traverse(ESON_SMALL, function (value, path, root) {
|
||||||
t.is(root, JSON_DATA_SMALL)
|
t.is(root, ESON_SMALL)
|
||||||
|
|
||||||
log.push([value, path, root])
|
log.push([value, path, root])
|
||||||
})
|
})
|
||||||
|
@ -669,11 +1014,11 @@ test('traverse', t => {
|
||||||
t.is(returnValue, undefined)
|
t.is(returnValue, undefined)
|
||||||
|
|
||||||
const EXPECTED_LOG = [
|
const EXPECTED_LOG = [
|
||||||
[JSON_DATA_SMALL, [], JSON_DATA_SMALL],
|
[ESON_SMALL, [], ESON_SMALL],
|
||||||
[JSON_DATA_SMALL.props[0].value, ['obj'], JSON_DATA_SMALL],
|
[ESON_SMALL.props[0].value, ['obj'], ESON_SMALL],
|
||||||
[JSON_DATA_SMALL.props[0].value.props[0].value, ['obj', 'a'], JSON_DATA_SMALL],
|
[ESON_SMALL.props[0].value.props[0].value, ['obj', 'a'], ESON_SMALL],
|
||||||
[JSON_DATA_SMALL.props[1].value, ['arr'], JSON_DATA_SMALL],
|
[ESON_SMALL.props[1].value, ['arr'], ESON_SMALL],
|
||||||
[JSON_DATA_SMALL.props[1].value.items[0].value, ['arr', '0'], JSON_DATA_SMALL],
|
[ESON_SMALL.props[1].value.items[0].value, ['arr', '0'], ESON_SMALL],
|
||||||
]
|
]
|
||||||
|
|
||||||
log.forEach((row, index) => {
|
log.forEach((row, index) => {
|
||||||
|
@ -684,51 +1029,51 @@ test('traverse', t => {
|
||||||
|
|
||||||
|
|
||||||
test('search', t => {
|
test('search', t => {
|
||||||
const searchResults = search(JSON_DATA_EXAMPLE, 'L')
|
const searchResults = search(ESON, 'L')
|
||||||
// printJSON(searchResults)
|
// printJSON(searchResults)
|
||||||
|
|
||||||
t.deepEqual(searchResults, [
|
t.deepEqual(searchResults, [
|
||||||
{path: ['obj', 'arr', '2', 'last'], type: 'property'},
|
{path: ['obj', 'arr', '2', 'last'], field: 'property'},
|
||||||
{path: ['str'], type: 'value'},
|
{path: ['str'], field: 'value'},
|
||||||
{path: ['nill'], type: 'property'},
|
{path: ['nill'], field: 'property'},
|
||||||
{path: ['nill'], type: 'value'},
|
{path: ['nill'], field: 'value'},
|
||||||
{path: ['bool'], type: 'property'},
|
{path: ['bool'], field: 'property'},
|
||||||
{path: ['bool'], type: 'value'}
|
{path: ['bool'], field: 'value'}
|
||||||
])
|
])
|
||||||
|
|
||||||
const activeSearchResult = searchResults[0]
|
const activeSearchResult = searchResults[0]
|
||||||
const updatedData = addSearchResults(JSON_DATA_EXAMPLE, searchResults, activeSearchResult)
|
const updatedData = applySearchResults(ESON, searchResults, activeSearchResult)
|
||||||
// printJSON(updatedData)
|
// printJSON(updatedData)
|
||||||
|
|
||||||
t.deepEqual(updatedData, JSON_DATA_EXAMPLE_SEARCH_L)
|
t.deepEqual(updatedData, ESON_SEARCH_L)
|
||||||
})
|
})
|
||||||
|
|
||||||
test('nextSearchResult', t => {
|
test('nextSearchResult', t => {
|
||||||
const searchResults = [
|
const searchResults = [
|
||||||
{path: ['obj', 'arr', '2', 'last'], type: 'property'},
|
{path: ['obj', 'arr', '2', 'last'], field: 'property'},
|
||||||
{path: ['str'], type: 'value'},
|
{path: ['str'], field: 'value'},
|
||||||
{path: ['nill'], type: 'property'},
|
{path: ['nill'], field: 'property'},
|
||||||
{path: ['nill'], type: 'value'},
|
{path: ['nill'], field: 'value'},
|
||||||
{path: ['bool'], type: 'property'},
|
{path: ['bool'], field: 'property'},
|
||||||
{path: ['bool'], type: 'value'}
|
{path: ['bool'], field: 'value'}
|
||||||
]
|
]
|
||||||
|
|
||||||
t.deepEqual(nextSearchResult(searchResults,
|
t.deepEqual(nextSearchResult(searchResults,
|
||||||
{path: ['nill'], type: 'property'}),
|
{path: ['nill'], field: 'property'}),
|
||||||
{path: ['nill'], type: 'value'})
|
{path: ['nill'], field: 'value'})
|
||||||
|
|
||||||
// wrap around
|
// wrap around
|
||||||
t.deepEqual(nextSearchResult(searchResults,
|
t.deepEqual(nextSearchResult(searchResults,
|
||||||
{path: ['bool'], type: 'value'}),
|
{path: ['bool'], field: 'value'}),
|
||||||
{path: ['obj', 'arr', '2', 'last'], type: 'property'})
|
{path: ['obj', 'arr', '2', 'last'], field: 'property'})
|
||||||
|
|
||||||
// return first when current is not found
|
// return first when current is not found
|
||||||
t.deepEqual(nextSearchResult(searchResults,
|
t.deepEqual(nextSearchResult(searchResults,
|
||||||
{path: ['non', 'existing'], type: 'value'}),
|
{path: ['non', 'existing'], field: 'value'}),
|
||||||
{path: ['obj', 'arr', '2', 'last'], type: 'property'})
|
{path: ['obj', 'arr', '2', 'last'], field: 'property'})
|
||||||
|
|
||||||
// return null when searchResults are empty
|
// return null when searchResults are empty
|
||||||
t.deepEqual(nextSearchResult([], {path: ['non', 'existing'], type: 'value'}), null)
|
t.deepEqual(nextSearchResult([], {path: ['non', 'existing'], field: 'value'}), null)
|
||||||
})
|
})
|
||||||
|
|
||||||
test('previousSearchResult', t => {
|
test('previousSearchResult', t => {
|
||||||
|
@ -759,6 +1104,49 @@ test('previousSearchResult', t => {
|
||||||
t.deepEqual(previousSearchResult([], {path: ['non', 'existing'], type: 'value'}), null)
|
t.deepEqual(previousSearchResult([], {path: ['non', 'existing'], type: 'value'}), null)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
test('selection (object)', t => {
|
||||||
|
const selection = {
|
||||||
|
start: {path: ['obj', 'arr', '2', 'last']},
|
||||||
|
end: {path: ['nill']}
|
||||||
|
}
|
||||||
|
const result = applySelection(ESON, selection)
|
||||||
|
|
||||||
|
t.deepEqual(result, ESON_SELECTED_OBJECT)
|
||||||
|
})
|
||||||
|
|
||||||
|
test('selection (array)', t => {
|
||||||
|
const selection = {
|
||||||
|
start: {path: ['obj', 'arr', '1']},
|
||||||
|
end: {path: ['obj', 'arr', '0']} // note the "wrong" order of start and end
|
||||||
|
}
|
||||||
|
|
||||||
|
const result = applySelection(ESON, selection)
|
||||||
|
|
||||||
|
t.deepEqual(result, ESON_SELECTED_ARRAY)
|
||||||
|
})
|
||||||
|
|
||||||
|
test('selection (value)', t => {
|
||||||
|
const selection = {
|
||||||
|
start: {path: ['obj', 'arr', '2', 'first']},
|
||||||
|
end: {path: ['obj', 'arr', '2', 'first']}
|
||||||
|
}
|
||||||
|
|
||||||
|
const result = applySelection(ESON, selection)
|
||||||
|
|
||||||
|
t.deepEqual(result, ESON_SELECTED_VALUE)
|
||||||
|
})
|
||||||
|
|
||||||
|
test('selection (single parent)', t => {
|
||||||
|
const selection = {
|
||||||
|
start: {path: ['obj', 'arr', '2']},
|
||||||
|
end: {path: ['obj', 'arr', '2']}
|
||||||
|
}
|
||||||
|
|
||||||
|
const result = applySelection(ESON, selection)
|
||||||
|
|
||||||
|
t.deepEqual(result, ESON_SELECTED_PARENT)
|
||||||
|
})
|
||||||
|
|
||||||
// helper function to replace all id properties with a constant value
|
// helper function to replace all id properties with a constant value
|
||||||
function replaceIds (data, value = '[ID]') {
|
function replaceIds (data, value = '[ID]') {
|
||||||
if (data.type === 'Object') {
|
if (data.type === 'Object') {
|
||||||
|
|
Loading…
Reference in New Issue