mirror of
https://github.com/Foundry376/Mailspring.git
synced 2025-09-07 05:04:58 +08:00
fix(composer): much better specs for composer & quoted text
Summary: Fixed a bug bug with the quoted text clearing the bodies on replies Test Plan: all the tests Reviewers: dillon, bengotow Reviewed By: bengotow Differential Revision: https://phab.nylas.com/D1981
This commit is contained in:
parent
1daeaff367
commit
ee41722891
9 changed files with 246 additions and 142 deletions
|
@ -106,9 +106,17 @@ class ContenteditableComponent extends React.Component
|
|||
onInput={@_onInput}
|
||||
onKeyDown={@_onKeyDown}
|
||||
dangerouslySetInnerHTML={@_dangerouslySetInnerHTML()}></div>
|
||||
<a className={@_quotedTextClasses()} onClick={@_onToggleQuotedText}></a>
|
||||
{@_renderQuotedTextControl()}
|
||||
</div>
|
||||
|
||||
_renderQuotedTextControl: ->
|
||||
if QuotedHTMLParser.hasQuotedHTML(@props.html)
|
||||
text = if @props.mode?.showQuotedText then "Hide" else "Show"
|
||||
<a className="quoted-text-control" onClick={@_onToggleQuotedText}>
|
||||
<span className="dots">•••</span>{text} previous
|
||||
</a>
|
||||
else return null
|
||||
|
||||
focus: =>
|
||||
@_editableNode().focus()
|
||||
|
||||
|
@ -1096,7 +1104,5 @@ class ContenteditableComponent extends React.Component
|
|||
|
||||
_quotedTextClasses: => classNames
|
||||
"quoted-text-control": true
|
||||
"no-quoted-text": not QuotedHTMLParser.hasQuotedHTML(@props.html)
|
||||
"show-quoted-text": @props.mode?.showQuotedText
|
||||
|
||||
module.exports = ContenteditableComponent
|
||||
|
|
|
@ -107,9 +107,11 @@ useDraft = (draftAttributes={}) ->
|
|||
@draft = new Message _.extend({draft: true, body: ""}, draftAttributes)
|
||||
draft = @draft
|
||||
proxy = draftStoreProxyStub(DRAFT_CLIENT_ID, @draft)
|
||||
@proxy = proxy
|
||||
|
||||
|
||||
spyOn(ComposerView.prototype, "componentWillMount").andCallFake ->
|
||||
# NOTE: This is called in the context of the component.
|
||||
@_prepareForDraft(DRAFT_CLIENT_ID)
|
||||
@_setupSession(proxy)
|
||||
|
||||
|
@ -118,8 +120,7 @@ useDraft = (draftAttributes={}) ->
|
|||
# `componentWillMount`, we manually call sessionForClientId to make this
|
||||
# part of the test synchronous. We need to make the `then` block of the
|
||||
# sessionForClientId do nothing so `_setupSession` is not called twice!
|
||||
spyOn(DraftStore, "sessionForClientId").andCallFake ->
|
||||
then: ->
|
||||
spyOn(DraftStore, "sessionForClientId").andCallFake -> then: ->
|
||||
|
||||
useFullDraft = ->
|
||||
useDraft.call @,
|
||||
|
@ -141,6 +142,76 @@ describe "populated composer", ->
|
|||
@isSending = {state: false}
|
||||
spyOn(DraftStore, "isSendingDraft").andCallFake => @isSending.state
|
||||
|
||||
describe "when sending a new message", ->
|
||||
it 'makes a request with the message contents', ->
|
||||
useDraft.call @
|
||||
makeComposer.call @
|
||||
editableNode = React.findDOMNode(ReactTestUtils.findRenderedDOMComponentWithAttr(@composer, 'contentEditable'))
|
||||
spyOn(@proxy.changes, "add")
|
||||
editableNode.innerHTML = "Hello <strong>world</strong>"
|
||||
ReactTestUtils.Simulate.input(editableNode)
|
||||
expect(@proxy.changes.add).toHaveBeenCalled()
|
||||
expect(@proxy.changes.add.calls.length).toBe 1
|
||||
body = @proxy.changes.add.calls[0].args[0].body
|
||||
expect(body).toBe "<head></head><body>Hello <strong>world</strong></body>"
|
||||
|
||||
describe "when sending a reply-to message", ->
|
||||
beforeEach ->
|
||||
@replyBody = """<blockquote class="gmail_quote">On Sep 3 2015, at 12:14 pm, Evan Morikawa <evan@evanmorikawa.com> wrote:<br>This is a test!</blockquote>"""
|
||||
|
||||
useDraft.call @,
|
||||
from: [u1]
|
||||
to: [u2]
|
||||
subject: "Test Reply Message 1"
|
||||
body: @replyBody
|
||||
|
||||
makeComposer.call @
|
||||
@editableNode = React.findDOMNode(ReactTestUtils.findRenderedDOMComponentWithAttr(@composer, 'contentEditable'))
|
||||
spyOn(@proxy.changes, "add")
|
||||
|
||||
it 'begins with the replying message collapsed', ->
|
||||
expect(@editableNode.innerHTML).toBe ""
|
||||
|
||||
it 'saves the full new body, plus quoted text', ->
|
||||
@editableNode.innerHTML = "Hello <strong>world</strong>"
|
||||
ReactTestUtils.Simulate.input(@editableNode)
|
||||
expect(@proxy.changes.add).toHaveBeenCalled()
|
||||
expect(@proxy.changes.add.calls.length).toBe 1
|
||||
body = @proxy.changes.add.calls[0].args[0].body
|
||||
expect(body).toBe """<head></head><body>Hello <strong>world</strong>#{@replyBody}</body>"""
|
||||
|
||||
describe "when sending a forwarded message message", ->
|
||||
beforeEach ->
|
||||
@fwdBody = """<br><br><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex;">
|
||||
Begin forwarded message:
|
||||
<br><br>
|
||||
From: Evan Morikawa <evan@evanmorikawa.com><br>Subject: Test Forward Message 1<br>Date: Sep 3 2015, at 12:14 pm<br>To: Evan Morikawa <evan@nylas.com>
|
||||
<br><br>
|
||||
|
||||
<meta content="text/html; charset=us-ascii">This is a test!
|
||||
</blockquote>"""
|
||||
|
||||
useDraft.call @,
|
||||
from: [u1]
|
||||
to: [u2]
|
||||
subject: "Fwd: Test Forward Message 1"
|
||||
body: @fwdBody
|
||||
|
||||
makeComposer.call @
|
||||
@editableNode = React.findDOMNode(ReactTestUtils.findRenderedDOMComponentWithAttr(@composer, 'contentEditable'))
|
||||
spyOn(@proxy.changes, "add")
|
||||
|
||||
it 'begins with the forwarded message expanded', ->
|
||||
expect(@editableNode.innerHTML).toBe @fwdBody
|
||||
|
||||
it 'saves the full new body, plus forwarded text', ->
|
||||
@editableNode.innerHTML = "Hello <strong>world</strong>#{@fwdBody}"
|
||||
ReactTestUtils.Simulate.input(@editableNode)
|
||||
expect(@proxy.changes.add).toHaveBeenCalled()
|
||||
expect(@proxy.changes.add.calls.length).toBe 1
|
||||
body = @proxy.changes.add.calls[0].args[0].body
|
||||
expect(body).toBe """Hello <strong>world</strong>#{@fwdBody}"""
|
||||
|
||||
describe "When displaying info from a draft", ->
|
||||
beforeEach ->
|
||||
useFullDraft.apply(@)
|
||||
|
|
|
@ -11,106 +11,112 @@ ContenteditableComponent = require "../lib/contenteditable-component",
|
|||
describe "ContenteditableComponent", ->
|
||||
beforeEach ->
|
||||
@onChange = jasmine.createSpy('onChange')
|
||||
@html = 'Test <strong>HTML</strong><br>'
|
||||
@component = ReactTestUtils.renderIntoDocument(
|
||||
<ContenteditableComponent html={@html} onChange={@onChange}/>
|
||||
)
|
||||
@htmlNoQuote = 'Test <strong>HTML</strong><br>'
|
||||
@htmlWithQuote = 'Test <strong>HTML</strong><br><blockquote class="gmail_quote">QUOTE</blockquote>'
|
||||
|
||||
@htmlWithQuote = 'Test <strong>HTML</strong><br><br><blockquote class="gmail_quote">QUOTE</blockquote>'
|
||||
@componentWithQuote = ReactTestUtils.renderIntoDocument(
|
||||
<ContenteditableComponent html={@htmlWithQuote}
|
||||
onChange={@onChange}
|
||||
mode={showQuotedText: false}/>
|
||||
)
|
||||
# Must be called with the test's scope
|
||||
setHTML = (newHTML) ->
|
||||
@$contentEditable.innerHTML = newHTML
|
||||
ReactTestUtils.Simulate.input(@$contentEditable, {target: {value: newHTML}})
|
||||
|
||||
describe "quoted-text-control", ->
|
||||
it "should be rendered", ->
|
||||
expect(ReactTestUtils.findRenderedDOMComponentWithClass(@component, 'quoted-text-control')).toBeDefined()
|
||||
describe "quoted-text-control toggle button", ->
|
||||
|
||||
it "should be visible if the html contains quoted text", ->
|
||||
@toggle = ReactTestUtils.findRenderedDOMComponentWithClass(@componentWithQuote, 'quoted-text-control')
|
||||
expect(@toggle.props.className.indexOf('no-quoted-text') >= 0).toBe(false)
|
||||
|
||||
it "should be have `show-quoted-text` if showQuotedText is true", ->
|
||||
@componentWithQuote = ReactTestUtils.renderIntoDocument(
|
||||
<ContenteditableComponent html={@htmlWithQuote} onChange={@onChange} mode={showQuotedText: true}/>
|
||||
)
|
||||
@toggle = ReactTestUtils.findRenderedDOMComponentWithClass(@componentWithQuote, 'quoted-text-control')
|
||||
expect(@toggle.props.className.indexOf('show-quoted-text') >= 0).toBe(true)
|
||||
|
||||
it "should not have `show-quoted-text` if showQuotedText is false", ->
|
||||
@componentWithQuote.setState(showQuotedText: false)
|
||||
@toggle = ReactTestUtils.findRenderedDOMComponentWithClass(@componentWithQuote, 'quoted-text-control')
|
||||
expect(@toggle.props.className.indexOf('show-quoted-text') >= 0).toBe(false)
|
||||
|
||||
it "should be hidden otherwise", ->
|
||||
@toggle = ReactTestUtils.findRenderedDOMComponentWithClass(@component, 'quoted-text-control')
|
||||
expect(@toggle.props.className.indexOf('no-quoted-text') >= 0).toBe(true)
|
||||
|
||||
describe "when showQuotedText is false", ->
|
||||
it "should not display quoted text", ->
|
||||
@editDiv = ReactTestUtils.findRenderedDOMComponentWithAttr(@componentWithQuote, 'contentEditable')
|
||||
expect(React.findDOMNode(@editDiv).innerHTML).toEqual @html
|
||||
|
||||
describe "when showQuotedText is true", ->
|
||||
describe "when there's no quoted text", ->
|
||||
beforeEach ->
|
||||
@componentWithQuote = ReactTestUtils.renderIntoDocument(
|
||||
@contentEditable = ReactTestUtils.renderIntoDocument(
|
||||
<ContenteditableComponent html={@htmlNoQuote}
|
||||
onChange={@onChange}
|
||||
mode={showQuotedText: true}/>)
|
||||
@$contentEditable = React.findDOMNode(ReactTestUtils.findRenderedDOMComponentWithAttr(@contentEditable, 'contentEditable'))
|
||||
|
||||
it 'should not display any quoted text', ->
|
||||
expect(@$contentEditable.innerHTML).toBe @htmlNoQuote
|
||||
|
||||
it "allows the text to update", ->
|
||||
textToAdd = "MORE <strong>TEXT</strong>!"
|
||||
expect(@$contentEditable.innerHTML).toBe @htmlNoQuote
|
||||
setHTML.call(@, textToAdd + @htmlNoQuote)
|
||||
ev = @onChange.mostRecentCall.args[0]
|
||||
expect(ev.target.value).toEqual(textToAdd + @htmlNoQuote)
|
||||
|
||||
it 'should not render the quoted-text-control toggle', ->
|
||||
toggles = ReactTestUtils.scryRenderedDOMComponentsWithClass(@contentEditable, 'quoted-text-control')
|
||||
expect(toggles.length).toBe 0
|
||||
|
||||
|
||||
describe 'when showQuotedText is true', ->
|
||||
beforeEach ->
|
||||
@contentEditable = ReactTestUtils.renderIntoDocument(
|
||||
<ContenteditableComponent html={@htmlWithQuote}
|
||||
onChange={@onChange}
|
||||
mode={showQuotedText: true}/>
|
||||
)
|
||||
mode={showQuotedText: true}/>)
|
||||
@$contentEditable = React.findDOMNode(ReactTestUtils.findRenderedDOMComponentWithAttr(@contentEditable, 'contentEditable'))
|
||||
|
||||
it "should display all the HTML", ->
|
||||
@componentWithQuote.setState(showQuotedText: true)
|
||||
@editDiv = ReactTestUtils.findRenderedDOMComponentWithAttr(@componentWithQuote, 'contentEditable')
|
||||
expect(React.findDOMNode(@editDiv).innerHTML.indexOf('gmail_quote') >= 0).toBe(true)
|
||||
it 'should display the quoted text', ->
|
||||
expect(@$contentEditable.innerHTML).toBe @htmlWithQuote
|
||||
|
||||
describe "showQuotedText", ->
|
||||
it "should default to false", ->
|
||||
expect(@component.props.mode?.showQuotedText).toBeUndefined()
|
||||
it "should call `props.onChange` with the entire HTML string", ->
|
||||
textToAdd = "MORE <strong>TEXT</strong>!"
|
||||
expect(@$contentEditable.innerHTML).toBe @htmlWithQuote
|
||||
setHTML.call(@, textToAdd + @htmlWithQuote)
|
||||
ev = @onChange.mostRecentCall.args[0]
|
||||
expect(ev.target.value).toEqual(textToAdd + @htmlWithQuote)
|
||||
|
||||
describe "when the html is changed", ->
|
||||
beforeEach ->
|
||||
@changedHtmlWithoutQuote = '<head></head><body>Changed <strong>NEW 1 HTML</strong><br><br></body>'
|
||||
@changedHtmlWithQuote = 'Changed <strong>NEW 1 HTML</strong><br><br><blockquote class="gmail_quote">QUOTE</blockquote>'
|
||||
it "should allow the quoted text to be changed", ->
|
||||
newText = 'Test <strong>NEW 1 HTML</strong><blockquote class="gmail_quote">QUOTE CHANGED!!!</blockquote>'
|
||||
expect(@$contentEditable.innerHTML).toBe @htmlWithQuote
|
||||
setHTML.call(@, newText)
|
||||
ev = @onChange.mostRecentCall.args[0]
|
||||
expect(ev.target.value).toEqual(newText)
|
||||
|
||||
@performEdit = (newHTML, component = @componentWithQuote) =>
|
||||
editDiv = ReactTestUtils.findRenderedDOMComponentWithAttr(component, 'contentEditable')
|
||||
React.findDOMNode(editDiv).innerHTML = newHTML
|
||||
ReactTestUtils.Simulate.input(editDiv, {target: {value: newHTML}})
|
||||
|
||||
describe "when showQuotedText is true", ->
|
||||
describe 'quoted text control toggle button', ->
|
||||
beforeEach ->
|
||||
@componentWithQuote = ReactTestUtils.renderIntoDocument(
|
||||
<ContenteditableComponent html={@htmlWithQuote}
|
||||
onChange={@onChange}
|
||||
mode={showQuotedText: true}/>
|
||||
)
|
||||
@toggle = ReactTestUtils.findRenderedDOMComponentWithClass(@contentEditable, 'quoted-text-control')
|
||||
|
||||
it "should call `props.onChange` with the entire HTML string", ->
|
||||
@componentWithQuote.setState(showQuotedText: true)
|
||||
@performEdit(@changedHtmlWithQuote)
|
||||
ev = @onChange.mostRecentCall.args[0]
|
||||
expect(ev.target.value).toEqual(@changedHtmlWithQuote)
|
||||
it 'should be rendered', ->
|
||||
expect(@toggle).toBeDefined()
|
||||
|
||||
it "should allow the quoted text to be changed", ->
|
||||
changed = 'Test <strong>NEW 1 HTML</strong><blockquote class="gmail_quote">QUOTE CHANGED!!!</blockquote>'
|
||||
@componentWithQuote.setState(showQuotedText: true)
|
||||
@performEdit(changed)
|
||||
ev = @onChange.mostRecentCall.args[0]
|
||||
expect(ev.target.value).toEqual(changed)
|
||||
it 'prompts to hide the quote', ->
|
||||
expect(React.findDOMNode(@toggle).textContent).toEqual "•••Hide previous"
|
||||
|
||||
describe "when showQuotedText is false", ->
|
||||
it "should let you change the text, and then append the quoted text part to the end before firing `onChange`", ->
|
||||
@componentWithQuote.setState(showQuotedText: false)
|
||||
@performEdit(@changedHtmlWithoutQuote)
|
||||
ev = @onChange.mostRecentCall.args[0]
|
||||
withQuote = "<head></head><body>#{@changedHtmlWithQuote}</body>"
|
||||
expect(ev.target.value).toEqual(withQuote)
|
||||
describe 'when showQuotedText is false', ->
|
||||
beforeEach ->
|
||||
@contentEditable = ReactTestUtils.renderIntoDocument(
|
||||
<ContenteditableComponent html={@htmlWithQuote}
|
||||
onChange={@onChange}
|
||||
mode={showQuotedText: false}/>)
|
||||
@$contentEditable = React.findDOMNode(ReactTestUtils.findRenderedDOMComponentWithAttr(@contentEditable, 'contentEditable'))
|
||||
|
||||
it "should work if the component does not contain quoted text", ->
|
||||
changed = '<head></head><body>Hallooo! <strong>NEW 1 HTML HTML HTML</strong><br></body>'
|
||||
@component.setState(showQuotedText: true)
|
||||
@performEdit(changed, @component)
|
||||
ev = @onChange.mostRecentCall.args[0]
|
||||
expect(ev.target.value).toEqual(changed)
|
||||
# The quoted text dom parser wraps stuff inertly in body tags
|
||||
wrapBody = (html) -> "<head></head><body>#{html}</body>"
|
||||
|
||||
it 'should not display any quoted text', ->
|
||||
expect(@$contentEditable.innerHTML).toBe @htmlNoQuote
|
||||
|
||||
it "should let you change the text, and then append the quoted text part to the end before firing `onChange`", ->
|
||||
textToAdd = "MORE <strong>TEXT</strong>!"
|
||||
expect(@$contentEditable.innerHTML).toBe @htmlNoQuote
|
||||
setHTML.call(@, textToAdd + @htmlNoQuote)
|
||||
ev = @onChange.mostRecentCall.args[0]
|
||||
# Note that we expect the version WITH a quote while setting the
|
||||
# version withOUT a quote.
|
||||
expect(ev.target.value).toEqual(wrapBody(textToAdd + @htmlWithQuote))
|
||||
|
||||
it "should let you add more html that looks like quoted text, and still properly appends the old quoted text", ->
|
||||
textToAdd = "Yo <blockquote class=\"gmail_quote\">I'm a fake quote</blockquote>"
|
||||
expect(@$contentEditable.innerHTML).toBe @htmlNoQuote
|
||||
setHTML.call(@, textToAdd + @htmlNoQuote)
|
||||
ev = @onChange.mostRecentCall.args[0]
|
||||
# Note that we expect the version WITH a quote while setting the
|
||||
# version withOUT a quote.
|
||||
expect(ev.target.value).toEqual(wrapBody(textToAdd + @htmlWithQuote))
|
||||
|
||||
describe 'quoted text control toggle button', ->
|
||||
beforeEach ->
|
||||
@toggle = ReactTestUtils.findRenderedDOMComponentWithClass(@contentEditable, 'quoted-text-control')
|
||||
|
||||
it 'should be rendered', ->
|
||||
expect(@toggle).toBeDefined()
|
||||
|
||||
it 'prompts to hide the quote', ->
|
||||
expect(React.findDOMNode(@toggle).textContent).toEqual "•••Show previous"
|
||||
|
|
|
@ -35,7 +35,6 @@ class EmailFrame extends React.Component
|
|||
!_.isEqual(newProps, @props)
|
||||
|
||||
_writeContent: =>
|
||||
wrapperClass = if @props.showQuotedText then "show-quoted-text" else ""
|
||||
doc = React.findDOMNode(@).contentDocument
|
||||
doc.open()
|
||||
|
||||
|
@ -50,7 +49,7 @@ class EmailFrame extends React.Component
|
|||
EmailFixingStyles = EmailFixingStyles.replace(/.ignore-in-parent-frame/g, '')
|
||||
if (EmailFixingStyles)
|
||||
doc.write("<style>#{EmailFixingStyles}</style>")
|
||||
doc.write("<div id='inbox-html-wrapper' class='#{wrapperClass}'>#{@_emailContent()}</div>")
|
||||
doc.write("<div id='inbox-html-wrapper'>#{@_emailContent()}</div>")
|
||||
doc.close()
|
||||
|
||||
# Notify the EventedIFrame that we've replaced it's document (with `open`)
|
||||
|
@ -80,7 +79,7 @@ class EmailFrame extends React.Component
|
|||
if @props.showQuotedText
|
||||
@props.content
|
||||
else
|
||||
QuotedHTMLParser.hideQuotedHTML(@props.content)
|
||||
QuotedHTMLParser.removeQuotedHTML(@props.content, keepIfWholeBodyIsQuote: true)
|
||||
|
||||
|
||||
module.exports = EmailFrame
|
||||
|
|
|
@ -80,13 +80,21 @@ class MessageItem extends React.Component
|
|||
<div className="message-item-area">
|
||||
{@_renderHeader()}
|
||||
<EmailFrame showQuotedText={@state.showQuotedText} content={@_formatBody()}/>
|
||||
<a className={@_quotedTextClasses()} onClick={@_toggleQuotedText}></a>
|
||||
{@_renderQuotedTextControl()}
|
||||
{@_renderEvents()}
|
||||
{@_renderAttachments()}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
_renderQuotedTextControl: ->
|
||||
if QuotedHTMLParser.hasQuotedHTML(@props.message.body)
|
||||
text = if @state.showQuotedText then "Hide" else "Show"
|
||||
<a className="quoted-text-control" onClick={@_toggleQuotedText}>
|
||||
<span className="dots">•••</span>{text} previous
|
||||
</a>
|
||||
else return null
|
||||
|
||||
_renderHeader: =>
|
||||
classes = classNames
|
||||
"message-header": true
|
||||
|
@ -168,11 +176,6 @@ class MessageItem extends React.Component
|
|||
else
|
||||
<div></div>
|
||||
|
||||
_quotedTextClasses: => classNames
|
||||
"quoted-text-control": true
|
||||
'no-quoted-text': not QuotedHTMLParser.hasQuotedHTML(@props.message.body)
|
||||
'show-quoted-text': @state.showQuotedText
|
||||
|
||||
_renderHeaderSideItems: ->
|
||||
styles =
|
||||
position: "absolute"
|
||||
|
|
|
@ -246,21 +246,33 @@ describe "MessageItem", ->
|
|||
|
||||
|
||||
describe "showQuotedText", ->
|
||||
|
||||
it "should be initialized to false", ->
|
||||
@createComponent()
|
||||
expect(@component.state.showQuotedText).toBe(false)
|
||||
|
||||
it "should show the `show quoted text` toggle in the off state", ->
|
||||
it "shouldn't render the quoted text control if there's no quoted text", ->
|
||||
@message.body = "no quotes here!"
|
||||
@createComponent()
|
||||
toggle = ReactTestUtils.findRenderedDOMComponentWithClass(@component, 'quoted-text-control')
|
||||
expect(React.findDOMNode(toggle).className.indexOf('show-quoted-text')).toBe(-1)
|
||||
toggles = ReactTestUtils.scryRenderedDOMComponentsWithClass(@component, 'quoted-text-control')
|
||||
expect(toggles.length).toBe 0
|
||||
|
||||
it "should have the `no quoted text` class if there is no quoted text in the message", ->
|
||||
spyOn(QuotedHTMLParser, 'hasQuotedHTML').andReturn false
|
||||
describe 'quoted text control toggle button', ->
|
||||
beforeEach ->
|
||||
@message.body = """
|
||||
Message
|
||||
<blockquote class="gmail_quote">
|
||||
Quoted message
|
||||
</blockquote>
|
||||
"""
|
||||
@createComponent()
|
||||
@toggle = ReactTestUtils.findRenderedDOMComponentWithClass(@component, 'quoted-text-control')
|
||||
|
||||
@createComponent()
|
||||
toggle = ReactTestUtils.findRenderedDOMComponentWithClass(@component, 'quoted-text-control')
|
||||
expect(React.findDOMNode(toggle).className.indexOf('no-quoted-text')).not.toBe(-1)
|
||||
it 'should be rendered', ->
|
||||
expect(@toggle).toBeDefined()
|
||||
|
||||
it 'prompts to hide the quote', ->
|
||||
expect(React.findDOMNode(@toggle).textContent).toEqual "•••Show previous"
|
||||
|
||||
it "should be initialized to true if the message contains `Forwarded`...", ->
|
||||
@message.body = """
|
||||
|
@ -294,14 +306,26 @@ describe "MessageItem", ->
|
|||
@createComponent()
|
||||
expect(@component.state.showQuotedText).toBe(false)
|
||||
|
||||
describe "when true", ->
|
||||
describe "when showQuotedText is true", ->
|
||||
beforeEach ->
|
||||
@message.body = """
|
||||
Message
|
||||
<blockquote class="gmail_quote">
|
||||
Quoted message
|
||||
</blockquote>
|
||||
"""
|
||||
@createComponent()
|
||||
@component.setState(showQuotedText: true)
|
||||
|
||||
it "should show the `show quoted text` toggle in the on state", ->
|
||||
toggle = ReactTestUtils.findRenderedDOMComponentWithClass(@component, 'quoted-text-control')
|
||||
expect(React.findDOMNode(toggle).className.indexOf('show-quoted-text') > 0).toBe(true)
|
||||
describe 'quoted text control toggle button', ->
|
||||
beforeEach ->
|
||||
@toggle = ReactTestUtils.findRenderedDOMComponentWithClass(@component, 'quoted-text-control')
|
||||
|
||||
it 'should be rendered', ->
|
||||
expect(@toggle).toBeDefined()
|
||||
|
||||
it 'prompts to hide the quote', ->
|
||||
expect(React.findDOMNode(@toggle).textContent).toEqual "•••Hide previous"
|
||||
|
||||
it "should pass the value into the EmailFrame", ->
|
||||
frame = ReactTestUtils.findRenderedComponentWithType(@component, EmailFrameStub)
|
||||
|
|
|
@ -12,8 +12,8 @@ describe "QuotedHTMLParser", ->
|
|||
hideQuotedHTML = (fname) ->
|
||||
return QuotedHTMLParser.hideQuotedHTML(readFile(fname))
|
||||
|
||||
removeQuotedHTML = (fname) ->
|
||||
return QuotedHTMLParser.removeQuotedHTML(readFile(fname))
|
||||
removeQuotedHTML = (fname, opts={}) ->
|
||||
return QuotedHTMLParser.removeQuotedHTML(readFile(fname), opts)
|
||||
|
||||
numQuotes = (html) ->
|
||||
re = new RegExp(QuotedHTMLParser.annotationClass, 'g')
|
||||
|
@ -21,7 +21,8 @@ describe "QuotedHTMLParser", ->
|
|||
|
||||
[1..16].forEach (n) ->
|
||||
it "properly parses email_#{n}", ->
|
||||
expect(removeQuotedHTML("email_#{n}.html")).toEqual readFile("email_#{n}_stripped.html")
|
||||
opts = keepIfWholeBodyIsQuote: true
|
||||
expect(removeQuotedHTML("email_#{n}.html", opts)).toEqual readFile("email_#{n}_stripped.html")
|
||||
|
||||
describe 'manual quote detection tests', ->
|
||||
|
||||
|
@ -258,7 +259,8 @@ describe "QuotedHTMLParser", ->
|
|||
|
||||
it 'works with these manual test cases', ->
|
||||
for {before, after} in tests
|
||||
test = clean(QuotedHTMLParser.removeQuotedHTML(before))
|
||||
opts = keepIfWholeBodyIsQuote: true
|
||||
test = clean(QuotedHTMLParser.removeQuotedHTML(before, opts))
|
||||
expect(test).toEqual clean(after)
|
||||
|
||||
it 'removes all trailing <br> tags except one', ->
|
||||
|
|
|
@ -8,20 +8,19 @@ class QuotedHTMLParser
|
|||
|
||||
# Given an html string, it will add the `annotationClass` to the DOM
|
||||
# element
|
||||
hideQuotedHTML: (html) ->
|
||||
hideQuotedHTML: (html, {keepIfWholeBodyIsQuote}={}) ->
|
||||
doc = @_parseHTML(html)
|
||||
quoteElements = @_findQuoteLikeElements(doc)
|
||||
if not @_wholeBodyIsQuote(doc, quoteElements)
|
||||
if keepIfWholeBodyIsQuote and @_wholeBodyIsQuote(doc, quoteElements)
|
||||
return doc.children[0].innerHTML
|
||||
else
|
||||
@_annotateElements(quoteElements)
|
||||
return doc.children[0].innerHTML
|
||||
return doc.children[0].innerHTML
|
||||
|
||||
hasQuotedHTML: (html) ->
|
||||
doc = @_parseHTML(html)
|
||||
quoteElements = @_findQuoteLikeElements(doc)
|
||||
if @_wholeBodyIsQuote(doc, quoteElements)
|
||||
return false
|
||||
else
|
||||
return quoteElements.length > 0
|
||||
return quoteElements.length > 0
|
||||
|
||||
# Public: Removes quoted text from an HTML string
|
||||
#
|
||||
|
@ -34,12 +33,17 @@ class QuotedHTMLParser
|
|||
# - `options`
|
||||
# - `includeInline` Defaults false. If true, inline quotes are removed
|
||||
# too
|
||||
# - `keepIfWholeBodyIsQuote` Defaults false. If true, then it will
|
||||
# check to see if the whole html body is a giant quote. If so, it will
|
||||
# preserve it.
|
||||
#
|
||||
# Returns HTML without quoted text
|
||||
removeQuotedHTML: (html, options={}) ->
|
||||
doc = @_parseHTML(html)
|
||||
quoteElements = @_findQuoteLikeElements(doc, options)
|
||||
if not @_wholeBodyIsQuote(doc, quoteElements)
|
||||
if options.keepIfWholeBodyIsQuote and @_wholeBodyIsQuote(doc, quoteElements)
|
||||
return doc.children[0].innerHTML
|
||||
else
|
||||
DOMUtils.removeElements(quoteElements, options)
|
||||
childNodes = doc.body.childNodes
|
||||
|
||||
|
@ -53,14 +57,13 @@ class QuotedHTMLParser
|
|||
break
|
||||
|
||||
DOMUtils.removeElements(extraTailBrTags)
|
||||
return doc.children[0].innerHTML
|
||||
return doc.children[0].innerHTML
|
||||
|
||||
appendQuotedHTML: (htmlWithoutQuotes, originalHTML) ->
|
||||
doc = @_parseHTML(originalHTML)
|
||||
quoteElements = @_findQuoteLikeElements(doc)
|
||||
if not @_wholeBodyIsQuote(doc, quoteElements)
|
||||
doc = @_parseHTML(htmlWithoutQuotes)
|
||||
doc.body.appendChild(node) for node in quoteElements
|
||||
doc = @_parseHTML(htmlWithoutQuotes)
|
||||
doc.body.appendChild(node) for node in quoteElements
|
||||
return doc.children[0].innerHTML
|
||||
|
||||
restoreAnnotatedHTML: (html) ->
|
||||
|
|
|
@ -10,29 +10,19 @@
|
|||
font-weight: 600;
|
||||
font-size: @font-size-smaller;
|
||||
|
||||
&.no-quoted-text {
|
||||
display:none;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
cursor: pointer;
|
||||
color: @text-color-subtle;
|
||||
border: 1px solid fade(@text-color-subtle, 35%);
|
||||
}
|
||||
|
||||
&:before {
|
||||
content:'\2022\2022\2022';
|
||||
.dots {
|
||||
display: inline-block;
|
||||
font-size: @font-size-smaller * 0.8;
|
||||
top:-1px;
|
||||
position:relative;
|
||||
padding-right:8px;
|
||||
}
|
||||
&.show-quoted-text:after {
|
||||
content:'Hide previous';
|
||||
}
|
||||
&:after {
|
||||
content:'Show previous';
|
||||
}
|
||||
}
|
||||
|
||||
.mail-label {
|
||||
|
|
Loading…
Add table
Reference in a new issue