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).
164 lines (150 loc) • 5.78 kB
JavaScript
import { test } from 'substance-test'
import {
CHILD, CHILDREN, Document, DocumentSchema, DocumentNode, map, pick
} from 'substance'
import simple from './fixture/simple'
import createTestArticle from './shared/createTestArticle'
test('Document: Create null selection.', function (t) {
const doc = createTestArticle(simple)
const sel = doc.createSelection(null)
t.ok(sel.isNull(), 'Selection should be null.')
t.end()
})
test('Document: Create collapsed property selection.', function (t) {
const doc = createTestArticle(simple)
const sel = doc.createSelection({
type: 'property',
path: ['p1', 'content'],
startOffset: 3
})
t.ok(sel.isPropertySelection(), 'Selection should be a property selection.')
t.ok(sel.isCollapsed(), 'Selection should be collapsed.')
t.deepEqual(sel.start.path, ['p1', 'content'], 'path should be correct.')
t.deepEqual(sel.start.offset, 3, 'start offset should be correct.')
t.end()
})
test('Document: Create expanded property selection.', function (t) {
const doc = createTestArticle(simple)
const sel = doc.createSelection({
type: 'property',
path: ['p1', 'content'],
startOffset: 1,
endOffset: 4
})
t.ok(sel.isPropertySelection(), 'Selection should be a property selection.')
t.notOk(sel.isCollapsed(), 'Selection should not be collapsed.')
t.deepEqual(sel.start.path, ['p1', 'content'], 'path should be correct.')
t.deepEqual(sel.start.offset, 1, 'start offset should be correct.')
t.deepEqual(sel.end.offset, 4, 'end offset should be correct.')
t.end()
})
test('Document: Node.toJSON() should not export undefined optional properties', function (t) {
const doc = createTestArticle(simple)
const p = doc.create({
type: 'paragraph',
id: 'p',
content: ''
})
t.deepEqual(p.toJSON(), { type: 'paragraph', id: 'p', content: '', textAlign: 'left' }, 'JSON should be correct.')
t.end()
})
test('Document: Setting a node property with DocumentNode.set()', t => {
const doc = createTestArticle(simple)
const p1 = doc.get('p1')
p1.set('content', 'XXX')
t.equal(p1.content, 'XXX', 'property should have changed')
t.end()
})
test('Document: Assigning multiple properties with DocumentNode.assign()', t => {
const doc = createTestArticle()
const node = doc.create({
type: 'structured-node',
id: 'sn'
})
const props = {
title: 'aaa',
body: 'bbb',
caption: 'ccc'
}
node.assign(props)
t.deepEqual(pick(node, ['title', 'body', 'caption']), props, 'properties should have changed')
t.end()
})
test('Document: node.find()', t => {
const doc = createTestArticle(simple)
const body = doc.get('body')
const p2 = body.find('#p2')
t.notNil(p2, 'body.find(#p2) should find a node')
t.equal(p2.id, 'p2', '.. with correct id')
doc.create({ type: 'strong', start: { path: p2.getPath(), offset: 1 }, end: { offset: 3 } })
const strong = p2.find('strong')
t.notNil(strong, 'p2.find(strong) should find a node')
t.equal(strong.type, 'strong', '.. of correct type')
t.end()
})
class Parent extends DocumentNode {
define () {
return {
type: 'parent',
child: CHILD('child')
}
}
}
class Child extends DocumentNode {
define () {
return {
type: 'child',
foo: { type: 'string', default: '' }
}
}
}
class ParentWithChildren extends DocumentNode {
define () {
return {
type: 'parent-with-children',
children: CHILDREN('child')
}
}
}
test('Document: resolve() a single id', t => {
const doc = new Document(new DocumentSchema({ nodes: [Parent, Child], DocumentClass: Document }))
const child = doc.create({ type: 'child', id: 'c1' })
doc.create({ type: 'parent', id: 'p1', child: 'c1' })
t.equal(doc.resolve(['p1', 'child']), child, 'resolve() should provide a referenced node')
t.end()
})
test('Document: resolve() multiple ids', t => {
const doc = new Document(new DocumentSchema({ nodes: [ParentWithChildren, Child], DocumentClass: Document }))
const c1 = doc.create({ type: 'child', id: 'c1' })
const c2 = doc.create({ type: 'child', id: 'c2' })
doc.create({ type: 'parent-with-children', id: 'p1', children: ['c1', 'c2'] })
t.deepEqual(doc.resolve(['p1', 'children']), [c1, c2], 'resolve() should provide referenced nodes')
t.end()
})
test('Document: resolve() provides values like get() for non-reference values', t => {
const doc = new Document(new DocumentSchema({ nodes: [Child], DocumentClass: Document }))
doc.create({ type: 'child', id: 'c1', foo: 'bar' })
t.equal(doc.resolve(['c1', 'foo']), 'bar', 'resolve() should provide a primitive values')
t.end()
})
test('Document: resolve() throws for non-existing properties in strict mode', t => {
const doc = new Document(new DocumentSchema({ nodes: [Child], DocumentClass: Document }))
doc.create({ type: 'child', id: 'c1', foo: 'bar' })
t.equal(doc.resolve(['c1', 'bla']), undefined, 'resolve() should return undefined in not-strict mode')
t.throws(() => {
doc.resolve(['c1', 'bla'], 'strict')
}, /Invalid path/, 'resolve() should throw for invalid paths in strict mode')
t.end()
})
test('Document: setting text', t => {
const doc = createTestArticle()
const p = doc.create({ type: 'paragraph', content: 'abcdefg' })
doc.create({ type: 'strong', start: { path: p.getPath(), offset: 1 }, end: { offset: 3 } })
const annos = map(p.getAnnotations())
t.equal(annos.length, 1, 'initially there should be one annotation on the paragraph')
p.setText('foo')
t.equal(p.content, 'foo', 'the text content should have been updated')
t.equal(map(p.getAnnotations()).length, 0, 'no annotation should be left on the paragraph')
t.nil(doc.get(annos[0].id), 'the old annotation should have been removed from the document')
t.end()
})