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.
456 lines (392 loc) • 17.5 kB
JavaScript
import { module } from 'substance-test'
import {
DefaultDOMElement, Registry, Clipboard,
ParagraphPackage, HeadingPackage, StrongPackage,
EmphasisPackage, LinkPackage, CodeblockPackage,
platform
} from 'substance'
const ParagraphHTMLConverter = ParagraphPackage.ParagraphHTMLConverter
const HeadingHTMLConverter = HeadingPackage.HeadingHTMLConverter
const StrongHTMLConverter = StrongPackage.StrongHTMLConverter
const EmphasisHTMLConverter = EmphasisPackage.EmphasisHTMLConverter
const LinkHTMLConverter = LinkPackage.LinkHTMLConverter
const CodeblockHTMLConverter = CodeblockPackage.CodeblockHTMLConverter
import simple from './fixture/simple'
import setupEditor from './fixture/setupEditor'
import BrowserLinuxPLainTextFixture from './fixture/html/browser-linux-plain-text'
import BrowserLinuxAnnotatedTextFixture from './fixture/html/browser-linux-annotated-text'
import BrowserLinuxTwoParagraphsFixture from './fixture/html/browser-linux-two-paragraphs'
import BrowserWindowsPlainTextFixture from './fixture/html/browser-windows-plain-text'
import BrowserWindowsAnnotatedTextFixture from './fixture/html/browser-windows-annotated-text'
import BrowserWindowsTwoParagraphsFixture from './fixture/html/browser-windows-two-paragraphs'
import BrowserLinuxFirefoxPlainTextFixture from './fixture/html/browser-linux-firefox-plain-text'
import BrowserLinuxFirefoxAnnotatedTextFixture from './fixture/html/browser-linux-firefox-annotated-text'
import BrowserLinuxFirefoxTwoParagraphsFixture from './fixture/html/browser-linux-firefox-two-paragraphs'
import BrowserLinuxFirefoxWholePageFixture from './fixture/html/browser-linux-firefox-whole-page'
import BrowserOSXFirefoxPlainTextFixture from './fixture/html/browser-osx-firefox-plain-text'
import BrowserOSXFirefoxAnnotatedTextFixture from './fixture/html/browser-osx-firefox-annotated-text'
import BrowserOSXFirefoxTwoParagraphsFixture from './fixture/html/browser-osx-firefox-two-paragraphs'
import BrowserWindowsFirefoxPlainTextFixture from './fixture/html/browser-windows-firefox-plain-text'
import BrowserWindowsFirefoxAnnotatedTextFixture from './fixture/html/browser-windows-firefox-annotated-text'
import BrowserWindowsFirefoxTwoParagraphsFixture from './fixture/html/browser-windows-firefox-two-paragraphs'
import BrowserWindowsEdgePlainTextFixture from './fixture/html/browser-windows-edge-plain-text'
import BrowserWindowsEdgeAnnotatedTextFixture from './fixture/html/browser-windows-edge-annotated-text'
import BrowserWindowsEdgeTwoParagraphsFixture from './fixture/html/browser-windows-edge-two-paragraphs'
import GDocsOSXLinuxChromePlainTextFixture from './fixture/html/google-docs-osx-linux-chrome-plain-text'
import GDocsOSXLinuxChromeAnnotatedTextFixture from './fixture/html/google-docs-osx-linux-chrome-annotated-text'
import GDocsOSXLinuxChromeTwoParagraphsFixture from './fixture/html/google-docs-osx-linux-chrome-two-paragraphs'
import GDocsLinuxFirefoxPlainTextFixture from './fixture/html/google-docs-linux-firefox-plain-text'
import GDocsLinuxFirefoxAnnotatedTextFixture from './fixture/html/google-docs-linux-firefox-annotated-text'
import GDocsOSXFirefoxPlainTextFixture from './fixture/html/google-docs-osx-firefox-plain-text'
import LibreOfficeOSXPlainTextFixture from './fixture/html/libre-office-osx-linux-plain-text'
import LibreOfficeOSXAnnotatedTextFixture from './fixture/html/libre-office-osx-linux-annotated-text'
import LibreOfficeOSXTwoParagraphsFixture from './fixture/html/libre-office-osx-linux-two-paragraphs'
import MSW11OSXPlainTextFixture from './fixture/html/word-11-osx-plain-text'
import MSW11OSXAnnotatedTextFixture from './fixture/html/word-11-osx-annotated-text'
import MSW11OSXTwoParagraphsFixture from './fixture/html/word-11-osx-two-paragraphs'
ClipboardTests()
if (platform.inBrowser) {
ClipboardTests('memory')
}
function ClipboardTests(memory) {
const test = module('Clipboard' + (memory ? ' [memory]' : ''), {
before: function() {
if (memory) platform.inBrowser = false
},
after: function() {
platform._reset()
}
})
test("Copying HTML, and plain text", function(t) {
let { editorSession, clipboard } = _fixture(t, simple)
editorSession.setSelection({ type: 'property', path: ['p1', 'content'], startOffset: 0, endOffset: 5 })
let event = new ClipboardEvent()
clipboard.onCopy(event)
let clipboardData = event.clipboardData
t.notNil(clipboardData.data['text/plain'], "Clipboard should contain plain text data.")
t.notNil(clipboardData.data['text/html'], "Clipboard should contain HTML data.")
let htmlDoc = DefaultDOMElement.parseHTML(clipboardData.data['text/html'])
let body = htmlDoc.find('body')
t.notNil(body, 'The copied HTML should always be a full HTML document string, containing a body element.')
t.end()
})
test("Copying a property selection", function(t) {
let { editorSession, clipboard } = _fixture(t, simple)
editorSession.setSelection({ type: 'property', path: ['p1', 'content'], startOffset: 0, endOffset: 5 })
let TEXT = '01234'
let event = new ClipboardEvent()
clipboard.onCopy(event)
let clipboardData = event.clipboardData
t.equal(clipboardData.data['text/plain'], TEXT, "Plain text should be correct.")
let htmlDoc = DefaultDOMElement.parseHTML(clipboardData.data['text/html'])
let body = htmlDoc.find('body')
t.equal(body.text(), TEXT, "HTML text should be correct.")
t.end()
})
test("Copying a container selection", function(t) {
let { editorSession, clipboard } = _fixture(t, simple)
editorSession.setSelection({
type: 'container',
containerId: 'body',
startPath: ['p1', 'content'],
startOffset: 1,
endPath: ['p3', 'content'],
endOffset: 5
})
let TEXT = [
'123456789',
'0123456789',
'01234'
]
let event = new ClipboardEvent()
clipboard.onCopy(event)
let clipboardData = event.clipboardData
t.equal(clipboardData.data['text/plain'], TEXT.join('\n'), "Plain text should be correct.")
let htmlDoc = DefaultDOMElement.parseHTML(clipboardData.data['text/html'])
let elements = htmlDoc.find('body').getChildren()
t.equal(elements.length, 3, "HTML should consist of three elements.")
let p1 = elements[0]
t.equal(p1.attr('data-id'), 'p1', "First element should have correct data-id.")
t.equal(p1.text(), TEXT[0], "First element should have correct text content.")
let p2 = elements[1]
t.equal(p2.attr('data-id'), 'p2', "Second element should have correct data-id.")
t.equal(p2.text(), TEXT[1], "Second element should have correct text content.")
let p3 = elements[2]
t.equal(p3.attr('data-id'), 'p3', "Third element should have correct data-id.")
t.equal(p3.text(), TEXT[2], "Third element should have correct text content.")
t.end()
})
test("Pasting text into ContainerEditor using 'text/plain'.", function(t) {
let { editorSession, clipboard, doc } = _fixture(t, simple)
editorSession.setSelection({
type: 'property',
path: ['p1', 'content'],
startOffset: 1,
containerId: 'body'
})
let event = new ClipboardEvent()
event.clipboardData.setData('text/plain', 'XXX')
clipboard.onPaste(event)
t.equal(doc.get(['p1', 'content']), '0XXX123456789', "Plain text should be correct.")
t.end()
})
test("Pasting without any data given.", function(t) {
let { editorSession, clipboard, doc } = _fixture(t, simple)
editorSession.setSelection({
type: 'property',
path: ['p1', 'content'],
startOffset: 1,
containerId: 'body'
})
let event = new ClipboardEvent()
clipboard.onPaste(event)
t.equal(doc.get(['p1', 'content']), '0123456789', "Text should be still the same.")
t.end()
})
test("Pasting text into ContainerEditor using 'text/html'.", function(t) {
let { editorSession, clipboard, doc } = _fixture(t, simple)
editorSession.setSelection({
type: 'property',
path: ['p1', 'content'],
startOffset: 1,
containerId: 'body'
})
let TEXT = 'XXX'
let event = new ClipboardEvent()
event.clipboardData.setData('text/plain', TEXT)
event.clipboardData.setData('text/html', TEXT)
clipboard.onPaste(event)
t.equal(doc.get(['p1', 'content']), '0XXX123456789', "Plain text should be correct.")
t.end()
})
// this test revealed #700: the problem was that in source code there where
// `"` and `'` characters which did not survive the way through HTML correctly
test("Copy and Pasting source code.", function(t) {
let { editorSession, clipboard, doc } = _fixture(t, simple)
let body = doc.get('body')
let cb = doc.create({
type: 'codeblock',
id: 'cb1',
content: [
"function hello_world() {",
" alert('Hello World!');",
"}"
].join("\n")
})
body.showAt(body.getPosition('p1')+1, cb)
editorSession.setSelection(doc.createSelection({
type: 'container',
startPath: ['p1', 'content'],
startOffset: 1,
endPath: ['p2', 'content'],
endOffset: 1,
containerId: 'body',
}))
let event = new ClipboardEvent()
clipboard.onCut(event)
let cb1 = doc.get('cb1')
t.isNil(cb1, "Codeblock should have been cutted.")
clipboard.onPaste(event)
cb1 = doc.get('cb1')
t.notNil(cb1, "Codeblock should have been pasted.")
t.deepEqual(cb1.toJSON(), cb.toJSON(), "Codeblock should have been pasted correctly.")
t.end()
})
test("Browser - Chrome (OSX/Linux) - Plain Text", function(t) {
_plainTextTest(t, BrowserLinuxPLainTextFixture)
})
test("Browser - Chrome (OSX/Linux) - Annotated Text", function(t) {
_annotatedTextTest(t, BrowserLinuxAnnotatedTextFixture)
})
test("Browser - Chrome (OSX/Linux) - Two Paragraphs", function(t) {
_twoParagraphsTest(t, BrowserLinuxTwoParagraphsFixture)
})
test("Browser - Chrome (Windows) - Plain Text", function(t) {
_plainTextTest(t, BrowserWindowsPlainTextFixture, 'forceWindows')
})
test("Browser - Chrome (Windows) - Annotated Text", function(t) {
_annotatedTextTest(t, BrowserWindowsAnnotatedTextFixture, 'forceWindows')
})
test("Browser - Chrome (Windows) - Two Paragraphs", function(t) {
_twoParagraphsTest(t, BrowserWindowsTwoParagraphsFixture, 'forceWindows')
})
test("Browser - Firefox (Linux) - Plain Text", function(t) {
_plainTextTest(t, BrowserLinuxFirefoxPlainTextFixture)
})
test("Browser - Firefox (Linux) - Annotated Text", function(t) {
_annotatedTextTest(t, BrowserLinuxFirefoxAnnotatedTextFixture)
})
test("Browser - Firefox (Linux) - Two Paragraphs", function(t) {
_twoParagraphsTest(t, BrowserLinuxFirefoxTwoParagraphsFixture)
})
test("Browser - Firefox (Linux) - Whole Page", function(t) {
let html = BrowserLinuxFirefoxWholePageFixture
_fixtureTest(t, html, function(doc, clipboard) {
let event = new ClipboardEvent()
event.clipboardData.setData('text/plain', 'XXX')
event.clipboardData.setData('text/html', html)
clipboard.onPaste(event)
// make sure HTML paste succeeded, by checking against the result of plain text insertion
t.notOk(doc.get('p1').getText() === '0XXX123456789', "HTML conversion and paste should have been successful (not fall back to plain-text).")
t.ok(doc.get('body').getLength() > 30, 'There should be a lot of paragraphs')
t.end()
})
})
test("Browser - Firefox (OSX) - Plain Text", function(t) {
_plainTextTest(t, BrowserOSXFirefoxPlainTextFixture)
})
test("Browser - Firefox (OSX) - Annotated Text", function(t) {
_annotatedTextTest(t, BrowserOSXFirefoxAnnotatedTextFixture)
})
test("Browser - Firefox (OSX) - Two Paragraphs", function(t) {
_twoParagraphsTest(t, BrowserOSXFirefoxTwoParagraphsFixture)
})
test("Browser - Firefox (Windows) - Plain Text", function(t) {
_plainTextTest(t, BrowserWindowsFirefoxPlainTextFixture, 'forceWindows')
})
test("Browser - Firefox (Windows) - Annotated Text", function(t) {
_annotatedTextTest(t, BrowserWindowsFirefoxAnnotatedTextFixture, 'forceWindows')
})
test("Browser - Firefox (Windows) - Two Paragraphs", function(t) {
_twoParagraphsTest(t, BrowserWindowsFirefoxTwoParagraphsFixture, 'forceWindows')
})
test("Browser - Edge (Windows) - Plain Text", function(t) {
_plainTextTest(t, BrowserWindowsEdgePlainTextFixture, 'forceWindows')
})
test("Browser - Edge (Windows) - Annotated Text", function(t) {
_annotatedTextTest(t, BrowserWindowsEdgeAnnotatedTextFixture, 'forceWindows')
})
test("Browser - Edge (Windows) - Two Paragraphs", function(t) {
_twoParagraphsTest(t, BrowserWindowsEdgeTwoParagraphsFixture, 'forceWindows')
})
test("GoogleDocs - Chrome (OSX/Linux) - Plain Text", function(t) {
_plainTextTest(t, GDocsOSXLinuxChromePlainTextFixture)
})
test("GoogleDocs - Chrome (OSX/Linux) - Annotated Text", function(t) {
_annotatedTextTest(t, GDocsOSXLinuxChromeAnnotatedTextFixture)
})
test("GoogleDocs - Chrome (OSX/Linux) - Two Paragraphs", function(t) {
_twoParagraphsTest(t, GDocsOSXLinuxChromeTwoParagraphsFixture)
})
test("GoogleDocs - Firefox (Linux) - Plain Text", function(t) {
_plainTextTest(t, GDocsLinuxFirefoxPlainTextFixture)
})
test("GoogleDocs - Firefox (Linux) - Annotated Text", function(t) {
_annotatedTextTest(t, GDocsLinuxFirefoxAnnotatedTextFixture)
})
test("GoogleDocs - Firefox (OSX) - Plain Text", function(t) {
_plainTextTest(t, GDocsOSXFirefoxPlainTextFixture)
})
test("LibreOffice (OSX/Linux) - Plain Text", function(t) {
_plainTextTest(t, LibreOfficeOSXPlainTextFixture)
})
test("LibreOffice (OSX/Linux) - Annotated Text", function(t) {
_annotatedTextTest(t, LibreOfficeOSXAnnotatedTextFixture)
})
test("LibreOffice (OSX/Linux) - Two Paragraphs", function(t) {
_twoParagraphsTest(t, LibreOfficeOSXTwoParagraphsFixture)
})
test("Microsoft Word 11 (OSX) - Plain Text", function(t) {
_plainTextTest(t, MSW11OSXPlainTextFixture)
})
test("Microsoft Word 11 (OSX) - Annotated Text", function(t) {
_annotatedTextTest(t, MSW11OSXAnnotatedTextFixture)
})
test("Microsoft Word 11 (OSX) - Two Paragraphs", function(t) {
_twoParagraphsTest(t, MSW11OSXTwoParagraphsFixture)
})
}
let converterRegistry = new Registry({
"html": new Registry({
"paragraph": ParagraphHTMLConverter,
"heading": HeadingHTMLConverter,
"strong": StrongHTMLConverter,
"emphasis": EmphasisHTMLConverter,
"link": LinkHTMLConverter,
"codeblock": CodeblockHTMLConverter,
})
})
let clipboardConfig = {
converterRegistry: converterRegistry
}
class ClipboardEventData {
constructor() {
this.data = {}
}
getData(format) {
return this.data[format]
}
setData(format, data) {
this.data[format] = data
}
get types() {
return Object.keys(this.data)
}
}
class ClipboardEvent {
constructor() {
this.clipboardData = new ClipboardEventData()
}
preventDefault() {}
stopPropagation() {}
}
function _fixture(t, seed) {
let { editorSession, doc } = setupEditor(t, seed)
let clipboard = new Clipboard(editorSession, clipboardConfig)
return { editorSession, doc, clipboard }
}
function _fixtureTest(t, html, impl, forceWindows) {
let { editorSession, clipboard, doc } = _fixture(t, simple)
if (forceWindows) {
// NOTE: faking 'Windows' mode in importer so that
// the correct implementation will be used
clipboard.htmlImporter._isWindows = true
}
editorSession.setSelection({
type: 'property',
path: ['p1', 'content'],
startOffset: 1,
containerId: 'body'
})
impl(doc, clipboard)
}
function _plainTextTest(t, html, forceWindows) {
_fixtureTest(t, html, function(doc, clipboard) {
let event = new ClipboardEvent()
event.clipboardData.setData('text/plain', '')
event.clipboardData.setData('text/html', html)
clipboard.onPaste(event)
t.equal(doc.get(['p1', 'content']), '0XXX123456789', "Content should have been pasted correctly.")
t.end()
}, forceWindows)
}
function _annotatedTextTest(t, html, forceWindows) {
_fixtureTest(t, html, function(doc, clipboard) {
let event = new ClipboardEvent()
event.clipboardData.setData('text/plain', '')
event.clipboardData.setData('text/html', html)
clipboard.onPaste(event)
t.equal(doc.get(['p1', 'content']), '0XXX123456789', "Content should have been pasted correctly.")
let annotations = doc.getIndex('annotations').get(['p1', 'content'])
t.equal(annotations.length, 1, "There should be one annotation on the property now.")
let anno = annotations[0]
t.equal(anno.type, 'link', "The annotation should be a link.")
t.end()
}, forceWindows)
}
function _twoParagraphsTest(t, html, forceWindows) {
_fixtureTest(t, html, function(doc, clipboard) {
let event = new ClipboardEvent()
event.clipboardData.setData('text/plain', '')
event.clipboardData.setData('text/html', html)
clipboard.onPaste(event)
let body = doc.get('body')
let p1 = body.getChildAt(0)
t.equal(p1.content, '0AAA', "First paragraph should be truncated.")
let p2 = body.getChildAt(1)
t.equal(p2.content, 'BBB', "Second paragraph should contain 'BBB'.")
let p3 = body.getChildAt(2)
t.equal(p3.content, '123456789', "Remainder of original p1 should go into forth paragraph.")
t.end()
}, forceWindows)
}