2015-03-06 08:00:56 +08:00
# This tests the basic Contenteditable component. For various modules of
# the contenteditable (such as selection, tooltip, quoting, etc) see the
# related test files.
#
2015-05-20 07:06:59 +08:00
_ = require "underscore"
2015-06-09 03:41:31 +08:00
fs = require 'fs'
2015-02-19 06:24:16 +08:00
React = require "react/addons"
ReactTestUtils = React.addons.TestUtils
2015-03-21 08:51:49 +08:00
ContenteditableComponent = require "../lib/contenteditable-component",
2015-02-19 06:24:16 +08:00
2015-03-06 08:02:47 +08:00
describe "ContenteditableComponent", ->
2015-02-19 06:24:16 +08:00
beforeEach ->
@onChange = jasmine.createSpy('onChange')
html = 'Test <strong>HTML</strong>'
@component = ReactTestUtils.renderIntoDocument(
<ContenteditableComponent html={html} onChange={@onChange}/>
)
2015-04-25 02:33:10 +08:00
@editableNode = React.findDOMNode(ReactTestUtils.findRenderedDOMComponentWithAttr(@component, 'contentEditable'))
2015-02-19 06:24:16 +08:00
describe "render", ->
it 'should render into the document', ->
expect(ReactTestUtils.isCompositeComponentWithType @component, ContenteditableComponent).toBe true
it "should include a content-editable div", ->
2015-03-06 08:15:29 +08:00
expect(@editableNode).toBeDefined()
2015-02-19 06:24:16 +08:00
describe "when the html is changed", ->
beforeEach ->
@changedHtmlWithoutQuote = 'Changed <strong>NEW 1 HTML</strong><br>'
2015-03-06 08:00:56 +08:00
@performEdit = (newHTML, component = @component) =>
2015-03-06 08:15:29 +08:00
@editableNode.innerHTML = newHTML
ReactTestUtils.Simulate.input(@editableNode, {target: {value: newHTML}})
2015-02-19 06:24:16 +08:00
it "should fire `props.onChange`", ->
@performEdit('Test <strong>New HTML</strong>')
expect(@onChange).toHaveBeenCalled()
2015-03-03 07:33:58 +08:00
# One day we may make this more efficient. For now we aggressively
# re-render because of the manual cursor positioning.
it "should fire if the html is the same", ->
2015-02-19 06:24:16 +08:00
expect(@onChange.callCount).toBe(0)
@performEdit(@changedHtmlWithoutQuote)
expect(@onChange.callCount).toBe(1)
@performEdit(@changedHtmlWithoutQuote)
2015-03-03 07:33:58 +08:00
expect(@onChange.callCount).toBe(2)
2015-05-12 09:07:06 +08:00
2015-06-09 03:41:31 +08:00
describe "pasting", ->
beforeEach ->
describe "when a file item is present", ->
beforeEach ->
@mockEvent =
preventDefault: jasmine.createSpy('preventDefault')
clipboardData:
items: [{
kind: 'file'
type: 'image/png'
getAsFile: -> new Blob(['12341352312411'], {type : 'image/png'})
}]
it "should save the image to a temporary file and call `onFilePaste`", ->
onPaste = jasmine.createSpy('onPaste')
@component = ReactTestUtils.renderIntoDocument(
<ContenteditableComponent html={''} onChange={@onChange} onFilePaste={onPaste} />
)
@editableNode = React.findDOMNode(ReactTestUtils.findRenderedDOMComponentWithAttr(@component, 'contentEditable'))
runs ->
ReactTestUtils.Simulate.paste(@editableNode, @mockEvent)
waitsFor ->
onPaste.callCount > 0
runs ->
path = require('path')
file = onPaste.mostRecentCall.args[0]
expect(path.basename(file)).toEqual('Pasted File.png')
contents = fs.readFileSync(file)
expect(contents.toString()).toEqual('12341352312411')
describe "when html and plain text parts are present", ->
beforeEach ->
@mockEvent =
preventDefault: jasmine.createSpy('preventDefault')
clipboardData:
getData: ->
return '<strong>This is text</strong>' if 'text/html'
return 'This is plain text' if 'text/plain'
return null
items: [{
kind: 'string'
type: 'text/html'
getAsString: -> '<strong>This is text</strong>'
},{
kind: 'string'
type: 'text/plain'
getAsString: -> 'This is plain text'
}]
it "should sanitize the HTML string and call insertHTML", ->
spyOn(document, 'execCommand')
spyOn(@component, '_sanitizeInput').andCallThrough()
runs ->
ReactTestUtils.Simulate.paste(@editableNode, @mockEvent)
waitsFor ->
document.execCommand.callCount > 0
runs ->
expect(@component._sanitizeInput).toHaveBeenCalledWith('<strong>This is text</strong>', 'text/html')
[command, a, html] = document.execCommand.mostRecentCall.args
expect(command).toEqual('insertHTML')
expect(html).toEqual('<strong>This is text</strong>')
describe "when html and plain text parts are present", ->
beforeEach ->
@mockEvent =
preventDefault: jasmine.createSpy('preventDefault')
clipboardData:
getData: ->
return 'This is plain text' if 'text/plain'
return null
items: [{
kind: 'string'
type: 'text/plain'
getAsString: -> 'This is plain text'
}]
it "should sanitize the plain text string and call insertHTML", ->
spyOn(document, 'execCommand')
spyOn(@component, '_sanitizeInput').andCallThrough()
runs ->
ReactTestUtils.Simulate.paste(@editableNode, @mockEvent)
waitsFor ->
document.execCommand.callCount > 0
runs ->
expect(@component._sanitizeInput).toHaveBeenCalledWith('This is plain text', 'text/html')
[command, a, html] = document.execCommand.mostRecentCall.args
expect(command).toEqual('insertHTML')
expect(html).toEqual('This is plain text')
describe "sanitization", ->
2015-05-12 09:07:06 +08:00
tests = [
{
in: ""
sanitizedAsHTML: ""
sanitizedAsPlain: ""
},
{
in: "Hello World"
sanitizedAsHTML: "Hello World"
sanitizedAsPlain: "Hello World"
},
{
in: " Hello World"
# Should collapse to 1 space when rendered
sanitizedAsHTML: " Hello World"
# Preserving 2 spaces
sanitizedAsPlain: " Hello World"
},
{
in: " Hello World"
sanitizedAsHTML: " Hello World"
# Preserving 3 spaces
sanitizedAsPlain: " Hello World"
},
{
in: " Hello World"
sanitizedAsHTML: " Hello World"
# Preserving 4 spaces
sanitizedAsPlain: " Hello World"
},
{
in: "Hello\nWorld"
sanitizedAsHTML: "Hello<br />World"
# Convert newline to br
sanitizedAsPlain: "Hello<br/>World"
},
{
in: "Hello\rWorld"
sanitizedAsHTML: "Hello<br />World"
# Convert carriage return to br
sanitizedAsPlain: "Hello<br/>World"
},
{
in: "Hello\n\n\nWorld"
# Never have more than 2 br's in a row
sanitizedAsHTML: "Hello<br/><br/>World"
# Convert multiple newlines to same number of brs
sanitizedAsPlain: "Hello<br/><br/><br/>World"
},
{
in: "<style>Yo</style> Foo Bar <div>Baz</div>"
# Strip bad tags
2015-05-16 01:56:40 +08:00
sanitizedAsHTML: " Foo Bar Baz"
2015-05-12 09:07:06 +08:00
# HTML encode tags for literal display
sanitizedAsPlain: "<style>Yo</style> Foo Bar <div>Baz</div>"
2015-05-16 01:56:40 +08:00
},
2015-05-12 09:07:06 +08:00
{
in: "<script>Bah</script> Yo < script>Boo! < / script >"
# Strip non white-list tags and encode malformed ones.
sanitizedAsHTML: " Yo < script>Boo! < / script >"
# HTML encode tags for literal display
sanitizedAsPlain: "<script>Bah</script> Yo < script>Boo! < / script >"
2015-05-16 01:56:40 +08:00
},
{
in: """
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<meta http-equiv="Content-Style-Type" content="text/css">
<title></title>
<meta name="Generator" content="Cocoa HTML Writer">
<meta name="CocoaVersion" content="1265.21">
<style type="text/css">
li.li1 {margin: 0.0px 0.0px 0.0px 0.0px; font: 12.0px Helvetica}
ul.ul1 {list-style-type: disc}
</style>
</head>
<body>
<ul class="ul1">
<li class="li1"><b>Packet pickup: </b>I'll pick up my packet at some point on Saturday at Fort Mason. Let me know if you'd like me to get yours. I'll need a photo of your ID and your confirmation number. Also, shirt color preference, I believe. Gray or black? Can't remember...</li>
</ul>
</body>
</html>"""
# Strip non white-list tags and encode malformed ones.
sanitizedAsHTML: "<ul><br /><li><b>Packet pickup: </b>I'll pick up my packet at some point on Saturday at Fort Mason. Let me know if you'd like me to get yours. I'll need a photo of your ID and your confirmation number. Also, shirt color preference, I believe. Gray or black? Can't remember...</li><br /></ul>"
# HTML encode tags for literal display
sanitizedAsPlain: "<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd"><br/><html><br/><head><br/><meta http-equiv="Content-Type" content="text/html; charset=UTF-8"><br/><meta http-equiv="Content-Style-Type" content="text/css"><br/><title></title><br/><meta name="Generator" content="Cocoa HTML Writer"><br/><meta name="CocoaVersion" content="1265.21"><br/><style type="text/css"><br/>li.li1 {margin: 0.0px 0.0px 0.0px 0.0px; font: 12.0px Helvetica}<br/>ul.ul1 {list-style-type: disc}<br/></style><br/></head><br/><body><br/><ul class="ul1"><br/><li class="li1"><b>Packet pickup: </b>I'll pick up my packet at some point on Saturday at Fort Mason. Let me know if you'd like me to get yours. I'll need a photo of your ID and your confirmation number. Also, shirt color preference, I believe. Gray or black? Can't remember...</li><br/></ul><br/></body><br/></html>"
2015-05-12 09:07:06 +08:00
}
]
it "sanitizes plain text properly", ->
for test in tests
expect(@component._sanitizeInput(test.in, "text/plain")).toBe test.sanitizedAsPlain
it "sanitizes html text properly", ->
for test in tests
expect(@component._sanitizeInput(test.in, "text/html")).toBe test.sanitizedAsHTML