diff --git a/internal_packages/composer/lib/contenteditable-component.cjsx b/internal_packages/composer/lib/contenteditable-component.cjsx index 2eac75315..85c4fc55f 100644 --- a/internal_packages/composer/lib/contenteditable-component.cjsx +++ b/internal_packages/composer/lib/contenteditable-component.cjsx @@ -106,9 +106,17 @@ class ContenteditableComponent extends React.Component onInput={@_onInput} onKeyDown={@_onKeyDown} dangerouslySetInnerHTML={@_dangerouslySetInnerHTML()}> - + {@_renderQuotedTextControl()} + _renderQuotedTextControl: -> + if QuotedHTMLParser.hasQuotedHTML(@props.html) + text = if @props.mode?.showQuotedText then "Hide" else "Show" + + •••{text} previous + + 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 diff --git a/internal_packages/composer/spec/composer-view-spec.cjsx b/internal_packages/composer/spec/composer-view-spec.cjsx index c8f5f167a..4dcb15e36 100644 --- a/internal_packages/composer/spec/composer-view-spec.cjsx +++ b/internal_packages/composer/spec/composer-view-spec.cjsx @@ -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 world" + 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 world" + + describe "when sending a reply-to message", -> + beforeEach -> + @replyBody = """On Sep 3 2015, at 12:14 pm, Evan Morikawa <evan@evanmorikawa.com> wrote:""" + + 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 world" + 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 world#{@replyBody}""" + + describe "when sending a forwarded message message", -> + beforeEach -> + @fwdBody = """
This is a test!
+ Begin forwarded message: +""" + + 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 world#{@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 world#{@fwdBody}""" + describe "When displaying info from a draft", -> beforeEach -> useFullDraft.apply(@) diff --git a/internal_packages/composer/spec/contenteditable-quoted-text-spec.cjsx b/internal_packages/composer/spec/contenteditable-quoted-text-spec.cjsx index 8c5177b95..bb98cf1b2 100644 --- a/internal_packages/composer/spec/contenteditable-quoted-text-spec.cjsx +++ b/internal_packages/composer/spec/contenteditable-quoted-text-spec.cjsx @@ -11,106 +11,112 @@ ContenteditableComponent = require "../lib/contenteditable-component", describe "ContenteditableComponent", -> beforeEach -> @onChange = jasmine.createSpy('onChange') - @html = 'Test HTML
+ From: Evan Morikawa <evan@evanmorikawa.com>
Subject: Test Forward Message 1
Date: Sep 3 2015, at 12:14 pm
To: Evan Morikawa <evan@nylas.com> +
+ + This is a test! +
QUOTE' - @htmlWithQuote = 'Test HTML
QUOTE' - @componentWithQuote = ReactTestUtils.renderIntoDocument( -
QUOTE' + it "should allow the quoted text to be changed", -> + newText = 'Test NEW 1 HTML
QUOTE CHANGED!!!' + 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( -
QUOTE CHANGED!!!' - @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 = "#{@changedHtmlWithQuote}" - expect(ev.target.value).toEqual(withQuote) + describe 'when showQuotedText is false', -> + beforeEach -> + @contentEditable = ReactTestUtils.renderIntoDocument( +
I'm a fake quote" + 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" diff --git a/internal_packages/message-list/lib/email-frame.cjsx b/internal_packages/message-list/lib/email-frame.cjsx index 1f6438749..98d8cff9f 100644 --- a/internal_packages/message-list/lib/email-frame.cjsx +++ b/internal_packages/message-list/lib/email-frame.cjsx @@ -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("") - doc.write("
+ Quoted message ++ """ + @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 +
+ Quoted message ++ """ @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) diff --git a/spec-nylas/quoted-html-parser-spec.coffee b/spec-nylas/quoted-html-parser-spec.coffee index 3851c8039..aa7a697a7 100644 --- a/spec-nylas/quoted-html-parser-spec.coffee +++ b/spec-nylas/quoted-html-parser-spec.coffee @@ -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