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 systems.
183 lines (163 loc) • 7.06 kB
JavaScript
import { module } from 'substance-test'
import { DefaultDOMElement, MemoryDOMElement, platform } from 'substance'
if (platform.inBrowser) {
DOMElementTests('BrowserDOMElement')
}
DOMElementTests('MemoryDOMElement')
function DOMElementTests(impl) {
const test = module('DefaultDOMElement ('+impl+')', {
before: () => {
if (impl === 'MemoryDOMElement') platform.inBrowser = false
},
after: () => {
platform._reset()
}
})
test("Wrapping a native element", (t) => {
let doc
if (impl === 'BrowserDOMElement') {
doc = window.document
} else {
doc = MemoryDOMElement.createDocument('html')
}
let nativeEl = doc.createElement('div')
let el = DefaultDOMElement.wrap(nativeEl)
t.ok(el._isDOMElement, 'native element should be wrapped')
let el2 = DefaultDOMElement.wrap(nativeEl)
t.ok(el === el2, 'wrapping an element twice should give the same DOMElement instance')
t.throws(() => {
DefaultDOMElement.wrap(undefined)
}, 'Should throw when trying to wrap nil')
t.end()
})
test("Creating an HTML document", (t) => {
const doc = DefaultDOMElement.createDocument('html')
t.notNil(doc.find('head'), 'Document should have a <head>')
t.notNil(doc.find('body'), '.. and a <body>')
t.equal(doc.getContentType(), 'text/html', '.. content type should be correct')
t.end()
})
test("Creating an XML document", (t) => {
const doc = DefaultDOMElement.createDocument('xml')
t.equal(doc.getContentType(), 'application/xml', 'Document should have correct content type')
t.equal(doc.children.length, 0, '.. and should be empty')
t.end()
})
test("Parsing a full HTML document", function(t) {
const html = '<html><head><title>TEST</title></head><body>TEST</body></html>'
const doc = DefaultDOMElement.parseHTML(html)
const head = doc.find('head')
t.notNil(head, '<head> should be there')
const title = head.find('title')
t.notNil(title, '<head> should contain <title>')
t.equal(title.text(), 'TEST', '.. with correct text')
const body = doc.find('body')
t.notNil(body, 'document should have a <body> element.')
t.equal(body.text(), 'TEST', '.. with correct text.')
t.end()
})
test("Parsing one HTML element", function(t) {
const html = '<p>TEST</p>'
const p = DefaultDOMElement.parseSnippet(html, 'html')
t.notNil(p, 'HTML should get parsed.')
t.equal(p.tagName, 'p', '.. providing one <p> element,')
t.equal(p.text(), 'TEST', '.. with correct content.')
t.end()
})
test("Parsing multiple HTML elements", function(t) {
const html = '<p>TEST</p><p>TEST2</p>'
const els = DefaultDOMElement.parseSnippet(html, 'html')
t.notNil(els, 'HTML should get parsed.')
t.equal(els.length, 2, '.. Providing 2 elements')
t.equal(els[0].tagName, 'p', '.. the first a <p>')
t.equal(els[0].text(), 'TEST', '.. with correct content')
t.equal(els[1].tagName, 'p', '.. the second a <p>')
t.equal(els[1].text(), 'TEST2', '.. with correct content')
t.end()
})
test("Parsing annotated HTML text", function(t) {
const html = '123<b>456</b>789'
const els = DefaultDOMElement.parseSnippet(html, 'html')
t.equal(els.length, 3, 'there are three elements')
t.equal(els[0].nodeType, 'text', 'first is a text node')
t.equal(els[0].text(), '123', '... it has correct content')
t.equal(els[1].nodeType, 'element', 'second is an element')
t.equal(els[1].tagName, 'b', '... it is a <b>')
t.equal(els[1].text(), '456', '... it has correct content')
t.equal(els[2].nodeType, 'text', 'third is a text node again')
t.equal(els[2].text(), '789', '... it has correct content')
t.end()
})
test("Parsing an XML document", function(t) {
const xml = "<mydoc><myhead><mytitle>TEST</mytitle></myhead><mybody>TEST</mybody></mydoc>"
const doc = DefaultDOMElement.parseXML(xml)
const head = doc.find('myhead')
t.notNil(head, '<myhead> should be there')
const title = head.find('mytitle')
t.notNil(title, '<head> should contain <title>')
t.equal(title.text(), 'TEST', '.. with correct text')
const body = doc.find('mybody')
t.notNil(body, 'document should have a <body> element')
t.equal(body.text(), 'TEST', '.. with correct text')
t.end()
})
test("Parsing an incomplete HTML document", (t) => {
const doc = DefaultDOMElement.parseHTML('<title>Foo</title><p>TEST</p>')
const head = doc.find('head')
const title = head.find('title')
const body = doc.find('body')
t.notNil(head, 'document should have a <head>')
t.notNil(title, '<head> should contain <title>')
t.equal(title.textContent, 'Foo', 'title should be correct')
t.equal(body.innerHTML, '<p>TEST</p>', 'body should have contain the remaining content')
t.end()
})
test("Parsing an HTML document with comments", (t) => {
let doc = DefaultDOMElement.parseHTML('<body><!--TEST--></body>')
let body = doc.find('body')
let comment = body.firstChild
t.notNil(comment, 'there should a child element')
t.ok(comment.isCommentNode(), '.. which should be a comment')
t.equal(comment.textContent, 'TEST', '.. with correct content')
// multiple lines
doc = DefaultDOMElement.parseHTML('<body><!--\n TEST\n Foo\n Bar--></body>')
body = doc.find('body')
comment = body.firstChild
t.equal(comment.textContent, '\n TEST\n Foo\n Bar', 'multi-line comment should work too')
/*
NOTE: from looking at the default implementation in htmlparser2 it seems that they would merge subsequent comments
but actually this is not the case. So, there is a branch in oncomment() which I do not understand
The same with TextNodes.
```
if(lastTag && lastTag.type === ElementType.Comment){
lastTag.data += data
}
```
*/
// doc = DefaultDOMElement.parseHTML('<body><!--FOO--><!--BAR--></body>')
// body = doc.find('body')
// t.deepEqual(body.childNodes.map(c=>c.nodeType), ['comment','comment'], 'subsequent comments should not be merged')
t.end()
})
test("Parsing an XML document with CDATA", (t) => {
let foo = DefaultDOMElement.parseSnippet(`<foo><![CDATA[TEST]]></foo>`, 'xml')
let cdata = foo.firstChild
t.notNil(cdata, 'there should a child element')
t.equal(cdata.nodeType, 'cdata', '.. which should be CDATA')
t.equal(cdata.textContent, 'TEST', '.. with correct content')
t.end()
})
test("Parsing void and self-closing elements", (t) => {
t.throws(() => {
DefaultDOMElement.parseSnippet(`<foo>`, 'xml')
}, 'should not allow void elements in XML')
t.doesNotThrow(() => {
DefaultDOMElement.parseSnippet(`<foo />`, 'xml')
}, 'should allow self-closing elements in XML')
t.doesNotThrow(() => {
DefaultDOMElement.parseSnippet(`<input>`, 'html')
DefaultDOMElement.parseSnippet(`<input />`, 'html')
}, 'in HTML there are some allowed void elements, such as <input>')
t.end()
})
}