substance
Version:
Substance is a JavaScript library for web-based content editing. It provides building blocks for realizing custom text editors and web-based publishing system. It is developed to power our online editing platform [Substance](http://substance.io).
196 lines (179 loc) • 10.6 kB
JavaScript
import { test } from 'substance-test'
import {
Document, DocumentSchema, DocumentNode, CHILD, OPTIONAL, CHILDREN, TextNode,
PropertyAnnotation
} from 'substance'
/*
Tests:
- CHILDREN relationship
- parents should change when setting children array
- XPATH
similar to above (maybe check xpath in other tests already)
*/
class Parent extends DocumentNode {
define () {
return {
type: 'parent',
foo: OPTIONAL(CHILD('child'))
}
}
}
class Child extends DocumentNode {
define () {
return {
type: 'child'
}
}
}
class SomeText extends TextNode {
define () {
return { type: 'some-text' }
}
}
class SomeAnno extends PropertyAnnotation {
define () {
return { type: 'some-anno' }
}
}
test('ParentNode: nodes referenced via CHILD should have parent set after creation', t => {
const doc = new Document(new DocumentSchema({ nodes: [Parent, Child], DocumentClass: Document }))
const child = doc.create({ type: 'child', id: 'b' })
const parent = doc.create({ type: 'parent', id: 'a', foo: 'b' })
t.ok(child.getParent() === parent, 'child should have parent')
t.deepEqual(child.getXpath().toArray(), [{ id: 'a', type: 'parent' }, { id: 'b', type: 'child', property: 'foo' }], 'xpath of child node should be correct')
t.end()
})
test('ParentNode: CHILD should have parent even when created in wrong order', t => {
const doc = new Document(new DocumentSchema({ nodes: [Parent, Child], DocumentClass: Document }))
const parent = doc.create({ type: 'parent', id: 'a', foo: 'b' })
const child = doc.create({ type: 'child', id: 'b' })
t.ok(child.getParent() === parent, 'child should have parent')
t.deepEqual(child.getXpath().toArray(), [{ id: 'a', type: 'parent' }, { id: 'b', type: 'child', property: 'foo' }], 'xpath of child node should be correct')
t.end()
})
test('ParentNode: setting CHILD to null should remove parent link', t => {
const doc = new Document(new DocumentSchema({ nodes: [Parent, Child], DocumentClass: Document }))
const child = doc.create({ type: 'child', id: 'b' })
doc.create({ type: 'parent', id: 'a', foo: 'b' })
doc.set(['a', 'foo'], null)
t.isNil(child.getParent(), 'child should have no parent')
t.deepEqual(child.getXpath().toArray(), [{ id: 'b', type: 'child' }], 'xpath of child node should be correct')
t.end()
})
test('ParentNode: parents should be updated when replacing a CHILD', t => {
const doc = new Document(new DocumentSchema({ nodes: [Parent, Child], DocumentClass: Document }))
const child1 = doc.create({ type: 'child', id: 'b' })
const child2 = doc.create({ type: 'child', id: 'c' })
const parent = doc.create({ type: 'parent', id: 'a' })
doc.set(['a', 'foo'], child1.id)
t.ok(child1.getParent() === parent, 'child1 should have parent')
t.isNil(child2.getParent(), 'child2 should have no parent')
t.deepEqual(child1.getXpath().toArray(), [{ id: 'a', type: 'parent' }, { id: 'b', type: 'child', property: 'foo' }], 'xpath of child1 should be correct')
t.deepEqual(child2.getXpath().toArray(), [{ id: 'c', type: 'child' }], 'xpath of child2 should be correct')
doc.set(['a', 'foo'], child2.id)
t.isNil(child1.getParent(), 'child1 should have no parent')
t.ok(child2.getParent() === parent, 'child2 should have parent')
t.deepEqual(child1.getXpath().toArray(), [{ id: 'b', type: 'child' }], 'xpath of child1 should be correct')
t.deepEqual(child2.getXpath().toArray(), [{ id: 'a', type: 'parent' }, { id: 'c', type: 'child', property: 'foo' }], 'xpath of child2 should be correct')
t.end()
})
class ParentWithChildren extends DocumentNode {
define () {
return {
type: 'parent',
foo: CHILDREN('child')
}
}
}
test('ParentNode: CHILDREN should have parent', t => {
const doc = new Document(new DocumentSchema({ nodes: [ParentWithChildren, Child], DocumentClass: Document }))
const child1 = doc.create({ type: 'child', id: 'b' })
const child2 = doc.create({ type: 'child', id: 'c' })
const parent = doc.create({ type: 'parent', id: 'a', foo: ['b', 'c'] })
t.ok(child1.getParent() === parent, 'child1 should have parent')
t.ok(child2.getParent() === parent, 'child2 should have parent')
t.deepEqual(child1.getXpath().toArray(), [{ id: 'a', type: 'parent' }, { id: 'b', type: 'child', property: 'foo', pos: 0 }], 'xpath of child1 should be correct')
t.deepEqual(child2.getXpath().toArray(), [{ id: 'a', type: 'parent' }, { id: 'c', type: 'child', property: 'foo', pos: 1 }], 'xpath of child2 should be correct')
t.end()
})
test('ParentNode: CHILDREN should have parent even if created in wrong order', t => {
const doc = new Document(new DocumentSchema({ nodes: [ParentWithChildren, Child], DocumentClass: Document }))
const parent = doc.create({ type: 'parent', id: 'a', foo: ['b', 'c'] })
const child1 = doc.create({ type: 'child', id: 'b' })
const child2 = doc.create({ type: 'child', id: 'c' })
t.ok(child1.getParent() === parent, 'child1 should have parent')
t.ok(child2.getParent() === parent, 'child2 should have parent')
t.deepEqual(child1.getXpath().toArray(), [{ id: 'a', type: 'parent' }, { id: 'b', type: 'child', property: 'foo', pos: 0 }], 'xpath of child1 should be correct')
t.deepEqual(child2.getXpath().toArray(), [{ id: 'a', type: 'parent' }, { id: 'c', type: 'child', property: 'foo', pos: 1 }], 'xpath of child2 should be correct')
t.end()
})
test('ParentNode: inserted CHILDREN should have parent', t => {
const doc = new Document(new DocumentSchema({ nodes: [ParentWithChildren, Child], DocumentClass: Document }))
const parent = doc.create({ type: 'parent', id: 'a' })
const child1 = doc.create({ type: 'child', id: 'b' })
doc.update([parent.id, 'foo'], { type: 'insert', pos: 0, value: child1.id })
t.ok(child1.getParent() === parent, 'child1 should have parent')
t.deepEqual(child1.getXpath().toArray(), [{ id: 'a', type: 'parent' }, { id: 'b', type: 'child', property: 'foo', pos: 0 }], 'xpath of child1 should be correct')
const child2 = doc.create({ type: 'child', id: 'c' })
doc.update([parent.id, 'foo'], { type: 'insert', pos: 0, value: child2.id })
t.ok(child2.getParent() === parent, 'child2 should have parent')
t.deepEqual(child1.getXpath().toArray(), [{ id: 'a', type: 'parent' }, { id: 'b', type: 'child', property: 'foo', pos: 1 }], 'xpath of child1 should be correct')
t.deepEqual(child2.getXpath().toArray(), [{ id: 'a', type: 'parent' }, { id: 'c', type: 'child', property: 'foo', pos: 0 }], 'xpath of child2 should be correct')
t.end()
})
test('ParentNode: removed CHILDREN should have no parent', t => {
const doc = new Document(new DocumentSchema({ nodes: [ParentWithChildren, Child], DocumentClass: Document }))
const child1 = doc.create({ type: 'child', id: 'b' })
const child2 = doc.create({ type: 'child', id: 'c' })
const parent = doc.create({ type: 'parent', id: 'a', foo: ['b', 'c'] })
doc.update([parent.id, 'foo'], { type: 'delete', pos: 0 })
t.isNil(child1.getParent(), 'child1 should have no parent anymore')
t.ok(child2.getParent() === parent, 'child2 should have parent')
t.deepEqual(child1.getXpath().toArray(), [{ id: 'b', type: 'child' }], 'xpath of child1 should be correct')
t.deepEqual(child2.getXpath().toArray(), [{ id: 'a', type: 'parent' }, { id: 'c', type: 'child', property: 'foo', pos: 0 }], 'xpath of child2 should be correct')
t.end()
})
test('ParentNode: clearing CHILDREN should remove parent', t => {
const doc = new Document(new DocumentSchema({ nodes: [ParentWithChildren, Child], DocumentClass: Document }))
const child1 = doc.create({ type: 'child', id: 'b' })
const child2 = doc.create({ type: 'child', id: 'c' })
const parent = doc.create({ type: 'parent', id: 'a', foo: ['b', 'c'] })
doc.set([parent.id, 'foo'], [])
t.isNil(child1.getParent(), 'child1 should have no parent anymore')
t.isNil(child2.getParent(), 'child2 should have no parent anymore')
t.deepEqual(child1.getXpath().toArray(), [{ id: 'b', type: 'child' }], 'xpath of child1 should be correct')
t.deepEqual(child2.getXpath().toArray(), [{ id: 'c', type: 'child' }], 'xpath of child2 should be correct')
t.end()
})
test('ParentNode: setting CHILDREN should update parent appropriately', t => {
const doc = new Document(new DocumentSchema({ nodes: [ParentWithChildren, Child], DocumentClass: Document }))
const child1 = doc.create({ type: 'child', id: 'b' })
const child2 = doc.create({ type: 'child', id: 'c' })
const child3 = doc.create({ type: 'child', id: 'd' })
const parent = doc.create({ type: 'parent', id: 'a', foo: ['b', 'c'] })
doc.set([parent.id, 'foo'], ['c', 'd'])
t.isNil(child1.getParent(), 'child1 should have no parent anymore')
t.ok(child2.getParent() === parent, 'child2 should have parent')
t.ok(child3.getParent() === parent, 'child3 should have parent')
t.deepEqual(child1.getXpath().toArray(), [{ id: 'b', type: 'child' }], 'xpath of child1 should be correct')
t.deepEqual(child2.getXpath().toArray(), [{ id: 'a', type: 'parent' }, { id: 'c', type: 'child', property: 'foo', pos: 0 }], 'xpath of child2 should be correct')
t.deepEqual(child3.getXpath().toArray(), [{ id: 'a', type: 'parent' }, { id: 'd', type: 'child', property: 'foo', pos: 1 }], 'xpath of child3 should be correct')
t.end()
})
test('ParentNode: creating an annotation should set parent', t => {
const doc = new Document(new DocumentSchema({ nodes: [SomeText, SomeAnno], DocumentClass: Document }))
const text = doc.create({ type: 'some-text', id: 'text', content: 'abcdefgh' })
const anno = doc.create({ type: 'some-anno', id: 'anno', start: { path: ['text', 'content'], offset: 1 }, end: { offset: 3 } })
t.equal(anno.getParent(), text, 'annotation should have text node as parent')
t.deepEqual(anno.getXpath().toArray(), [{ id: 'text', type: 'some-text' }, { id: 'anno', type: 'some-anno', property: 'content' }], 'xpath of anno should be correct')
t.end()
})
test('ParentNode: setting the path of an annotation should update the parent accordingly', t => {
const doc = new Document(new DocumentSchema({ nodes: [SomeText, SomeAnno], DocumentClass: Document }))
doc.create({ type: 'some-text', id: 'text1', content: 'abcdefgh' })
const text2 = doc.create({ type: 'some-text', id: 'text2', content: 'abcdefgh' })
const anno = doc.create({ type: 'some-anno', id: 'anno', start: { path: ['text', 'content'], offset: 1 }, end: { offset: 3 } })
doc.set([anno.id, 'start', 'path'], ['text2', 'content'])
t.equal(anno.getParent(), text2, 'annotation should have text2 as parent')
t.end()
})