From 96da7ccb2ddd2edf78e2707df7846c2e74641e5b Mon Sep 17 00:00:00 2001
From: Juan Tejada
This is a test"
- replyToMessageId: null
-
-makeComposer = ->
- @composer = NylasTestUtils.renderIntoDocument(
- On Sep 3 2015, at 12:14 pm, Evan Morikawa <evan@evanmorikawa.com> wrote:
"""
+ @composer = ReactTestUtils.renderIntoDocument(
+
This is a test!
This is a test"
+ replyToMessageId: null
+
+ makeComposer = ->
+ @composer = NylasTestUtils.renderIntoDocument(
+
- Begin forwarded message:
-
"""
+ afterEach ->
+ DraftStore._cleanupAllSessions()
+ NylasTestUtils.removeFromDocument(@composer)
- useDraft.call @,
- from: [u1]
- to: [u2]
- subject: "Fwd: Test Forward Message 1"
- body: @fwdBody
+ 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"
+ @composer.refs[Fields.Body]._onDOMMutated(["mutated"])
+ 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"
- 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}"
- @composer.refs[Fields.Body]._onDOMMutated(["mutated"])
- 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(@)
- makeComposer.call(@)
-
- it "attaches the draft to the proxy", ->
- expect(@draft).toBeDefined()
- expect(@composer._proxy.draft()).toBe @draft
-
- it "sets the basic draft state", ->
- expect(@composer.state.from).toEqual [u1]
- expect(@composer.state.to).toEqual [u2]
- expect(@composer.state.cc).toEqual [u3, u4]
- expect(@composer.state.bcc).toEqual [u5]
- expect(@composer.state.subject).toEqual "Test Message 1"
- expect(@composer.state.files).toEqual [f1, f2]
- expect(@composer.state.body).toEqual "Hello World
- 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>
-
+ @isSending = false
+ spyOn(DraftStore, "isSendingDraft").andCallFake => @isSending
- This is a test!
-
This is a test"
-
- it "sets first-time initial state about focused fields", ->
- expect(@composer.state.populated).toBe true
- expect(@composer.state.focusedField).toBeDefined()
- expect(@composer.state.enabledFields).toBeDefined()
-
- it "sets first-time initial state about showing quoted text", ->
- expect(@composer.state.showQuotedText).toBe false
-
- describe "deciding which field is initially focused", ->
- it "focuses the To field if there's nobody in the 'to' field", ->
- useDraft.call @
- makeComposer.call @
- expect(@composer.state.focusedField).toBe Fields.To
-
- it "focuses the subject if there's no subject already", ->
- useDraft.call @, to: [u1]
- makeComposer.call @
- expect(@composer.state.focusedField).toBe Fields.Subject
-
- it "focuses the body otherwise", ->
- useDraft.call @, to: [u1], subject: "Yo"
- makeComposer.call @
- expect(@composer.state.focusedField).toBe Fields.Body
-
- describe "when deciding whether or not to enable the subject", ->
- it "enables the subject when the subject is empty", ->
- useDraft.call @, subject: ""
- makeComposer.call @
- expect(@composer._shouldEnableSubject()).toBe true
-
- it "enables the subject when the subject looks like a fwd", ->
- useDraft.call @, subject: "Fwd: This is the message"
- makeComposer.call @
- expect(@composer._shouldEnableSubject()).toBe true
-
- it "enables the subject when the subject looks like a fwd", ->
- useDraft.call @, subject: "fwd foo"
- makeComposer.call @
- expect(@composer._shouldEnableSubject()).toBe true
-
- it "doesn't enable subject when replyToMessageId exists", ->
- useDraft.call @, subject: "should hide", replyToMessageId: "some-id"
- makeComposer.call @
- expect(@composer._shouldEnableSubject()).toBe false
-
- it "enables the subject otherwise", ->
- useDraft.call @, subject: "Foo bar baz"
- makeComposer.call @
- expect(@composer._shouldEnableSubject()).toBe true
-
- describe "when deciding whether or not to enable cc and bcc", ->
- it "doesn't enable cc when there's no one to cc", ->
- useDraft.call @, cc: []
- makeComposer.call @
- expect(@composer.state.enabledFields).not.toContain Fields.Cc
-
- it "enables cc when populated", ->
- useDraft.call @, cc: [u1,u2]
- makeComposer.call @
- expect(@composer.state.enabledFields).toContain Fields.Cc
-
- it "doesn't enable bcc when there's no one to bcc", ->
- useDraft.call @, bcc: []
- makeComposer.call @
- expect(@composer.state.enabledFields).not.toContain Fields.Bcc
-
- it "enables bcc when populated", ->
- useDraft.call @, bcc: [u2,u3]
- makeComposer.call @
- expect(@composer.state.enabledFields).toContain Fields.Bcc
-
- describe "when deciding whether or not to enable the from field", ->
- it "disables if there's no draft", ->
- useDraft.call @
- makeComposer.call @
- expect(@composer._shouldShowFromField()).toBe false
-
- it "disables if account has no aliases", ->
- spyOn(AccountStore, 'itemWithId').andCallFake -> {id: 1, aliases: []}
- useDraft.call @, replyToMessageId: null, files: []
- makeComposer.call @
- expect(@composer.state.enabledFields).not.toContain Fields.From
-
- it "enables if it's a reply-to message", ->
- aliases = ['A {id: 1, aliases: aliases}
- useDraft.call @, replyToMessageId: "local-123", files: []
- makeComposer.call @
- expect(@composer.state.enabledFields).toContain Fields.From
-
- it "enables if requirements are met", ->
- a1 = new Account()
- a1.aliases = ['a1']
- spyOn(AccountStore, 'itemWithId').andCallFake -> a1
- useDraft.call @, replyToMessageId: null, files: []
- makeComposer.call @
- expect(@composer.state.enabledFields).toContain Fields.From
-
- describe "when enabling fields", ->
- it "always enables the To and Body fields on empty composers", ->
- useDraft.apply @
- makeComposer.call(@)
- expect(@composer.state.enabledFields).toContain Fields.To
- expect(@composer.state.enabledFields).toContain Fields.Body
-
- it "always enables the To and Body fields on full composers", ->
- useFullDraft.apply(@)
- makeComposer.call(@)
- expect(@composer.state.enabledFields).toContain Fields.To
- expect(@composer.state.enabledFields).toContain Fields.Body
-
- describe "applying the focused field", ->
- beforeEach ->
- useFullDraft.apply(@)
- makeComposer.call(@)
- @composer.setState focusedField: Fields.Cc
- @body = @composer.refs[Fields.Body]
- spyOn(@body, "focus")
- spyOn(React, "findDOMNode").andCallThrough()
- spyOn(@composer, "_applyFieldFocus").andCallThrough()
-
- it "can focus on the subject", ->
- @composer.setState focusedField: Fields.Subject
- expect(@composer._applyFieldFocus.calls.length).toBe 1
- expect(React.findDOMNode).toHaveBeenCalled()
- calls = _.filter React.findDOMNode.calls, (call) ->
- call.args[0].props.name == "subject"
- expect(calls.length).toBe 1
-
- it "can focus on the body", ->
- @composer.setState focusedField: Fields.Body
- expect(@body.focus).toHaveBeenCalled()
- expect(@body.focus.calls.length).toBe 1
-
- it "ignores focuses to participant fields", ->
- @composer.setState focusedField: Fields.To
- expect(@body.focus).not.toHaveBeenCalled()
- expect(@composer._applyFieldFocus.calls.length).toBe 1
-
- describe "when participants are added during a draft update", ->
- it "shows the cc fields and bcc fields to ensure participants are never hidden", ->
- useDraft.call(@, cc: [], bcc: [])
- makeComposer.call(@)
- expect(@composer.state.enabledFields).not.toContain Fields.Bcc
- expect(@composer.state.enabledFields).not.toContain Fields.Cc
-
- # Simulate a change event fired by the DraftStoreProxy
- @draft.cc = [u1]
- @composer._onDraftChanged()
-
- expect(@composer.state.enabledFields).not.toContain Fields.Bcc
- expect(@composer.state.enabledFields).toContain Fields.Cc
-
- # Simulate a change event fired by the DraftStoreProxy
- @draft.bcc = [u2]
- @composer._onDraftChanged()
- expect(@composer.state.enabledFields).toContain Fields.Bcc
- expect(@composer.state.enabledFields).toContain Fields.Cc
-
- describe "When sending a message", ->
- beforeEach ->
- spyOn(NylasEnv, "isMainWindow").andReturn true
- remote = require('remote')
- @dialog = remote.require('dialog')
- spyOn(remote, "getCurrentWindow")
- spyOn(@dialog, "showMessageBox")
- spyOn(Actions, "sendDraft")
-
- it "shows an error if there are no recipients", ->
- useDraft.call @, subject: "no recipients"
- makeComposer.call(@)
- @composer._sendDraft()
- expect(Actions.sendDraft).not.toHaveBeenCalled()
- expect(@dialog.showMessageBox).toHaveBeenCalled()
- dialogArgs = @dialog.showMessageBox.mostRecentCall.args[1]
- expect(dialogArgs.detail).toEqual("You need to provide one or more recipients before sending the message.")
- expect(dialogArgs.buttons).toEqual ['Edit Message']
-
- it "shows an error if a recipient is invalid", ->
- useDraft.call @,
- subject: 'hello world!'
- to: [new Contact(email: 'lol', name: 'lol')]
- makeComposer.call(@)
- @composer._sendDraft()
- expect(Actions.sendDraft).not.toHaveBeenCalled()
- expect(@dialog.showMessageBox).toHaveBeenCalled()
- dialogArgs = @dialog.showMessageBox.mostRecentCall.args[1]
- expect(dialogArgs.detail).toEqual("lol is not a valid email address - please remove or edit it before sending.")
- expect(dialogArgs.buttons).toEqual ['Edit Message']
-
- describe "empty body warning", ->
- it "warns if the body of the email is still the pristine body", ->
- pristineBody = "
"
+ 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 @,
- to: [u1]
- subject: "Hello World"
- body: pristineBody
+ 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"
+ @composer.refs[Fields.Body]._onDOMMutated(["mutated"])
+ 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}"
+ @composer.refs[Fields.Body]._onDOMMutated(["mutated"])
+ 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(@)
makeComposer.call(@)
- spyOn(@composer._proxy, 'draftPristineBody').andCallFake -> pristineBody
+ it "attaches the draft to the proxy", ->
+ expect(@draft).toBeDefined()
+ expect(@composer._proxy.draft()).toBe @draft
+ it "sets the basic draft state", ->
+ expect(@composer.state.from).toEqual [u1]
+ expect(@composer.state.to).toEqual [u2]
+ expect(@composer.state.cc).toEqual [u3, u4]
+ expect(@composer.state.bcc).toEqual [u5]
+ expect(@composer.state.subject).toEqual "Test Message 1"
+ expect(@composer.state.files).toEqual [f1, f2]
+ expect(@composer.state.body).toEqual "Hello World
+ 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!
+
This is a test"
+
+ it "sets first-time initial state about focused fields", ->
+ expect(@composer.state.populated).toBe true
+ expect(@composer.state.focusedField).toBeDefined()
+ expect(@composer.state.enabledFields).toBeDefined()
+
+ it "sets first-time initial state about showing quoted text", ->
+ expect(@composer.state.showQuotedText).toBe false
+
+ describe "deciding which field is initially focused", ->
+ it "focuses the To field if there's nobody in the 'to' field", ->
+ useDraft.call @
+ makeComposer.call @
+ expect(@composer.state.focusedField).toBe Fields.To
+
+ it "focuses the subject if there's no subject already", ->
+ useDraft.call @, to: [u1]
+ makeComposer.call @
+ expect(@composer.state.focusedField).toBe Fields.Subject
+
+ it "focuses the body otherwise", ->
+ useDraft.call @, to: [u1], subject: "Yo"
+ makeComposer.call @
+ expect(@composer.state.focusedField).toBe Fields.Body
+
+ describe "when deciding whether or not to enable the subject", ->
+ it "enables the subject when the subject is empty", ->
+ useDraft.call @, subject: ""
+ makeComposer.call @
+ expect(@composer._shouldEnableSubject()).toBe true
+
+ it "enables the subject when the subject looks like a fwd", ->
+ useDraft.call @, subject: "Fwd: This is the message"
+ makeComposer.call @
+ expect(@composer._shouldEnableSubject()).toBe true
+
+ it "enables the subject when the subject looks like a fwd", ->
+ useDraft.call @, subject: "fwd foo"
+ makeComposer.call @
+ expect(@composer._shouldEnableSubject()).toBe true
+
+ it "doesn't enable subject when replyToMessageId exists", ->
+ useDraft.call @, subject: "should hide", replyToMessageId: "some-id"
+ makeComposer.call @
+ expect(@composer._shouldEnableSubject()).toBe false
+
+ it "enables the subject otherwise", ->
+ useDraft.call @, subject: "Foo bar baz"
+ makeComposer.call @
+ expect(@composer._shouldEnableSubject()).toBe true
+
+ describe "when deciding whether or not to enable cc and bcc", ->
+ it "doesn't enable cc when there's no one to cc", ->
+ useDraft.call @, cc: []
+ makeComposer.call @
+ expect(@composer.state.enabledFields).not.toContain Fields.Cc
+
+ it "enables cc when populated", ->
+ useDraft.call @, cc: [u1,u2]
+ makeComposer.call @
+ expect(@composer.state.enabledFields).toContain Fields.Cc
+
+ it "doesn't enable bcc when there's no one to bcc", ->
+ useDraft.call @, bcc: []
+ makeComposer.call @
+ expect(@composer.state.enabledFields).not.toContain Fields.Bcc
+
+ it "enables bcc when populated", ->
+ useDraft.call @, bcc: [u2,u3]
+ makeComposer.call @
+ expect(@composer.state.enabledFields).toContain Fields.Bcc
+
+ describe "when deciding whether or not to enable the from field", ->
+ it "disables if there's no draft", ->
+ useDraft.call @
+ makeComposer.call @
+ expect(@composer._shouldShowFromField()).toBe false
+
+ it "disables if account has no aliases", ->
+ spyOn(AccountStore, 'itemWithId').andCallFake -> {id: 1, aliases: []}
+ useDraft.call @, replyToMessageId: null, files: []
+ makeComposer.call @
+ expect(@composer.state.enabledFields).not.toContain Fields.From
+
+ it "enables if it's a reply-to message", ->
+ aliases = ['A {id: 1, aliases: aliases}
+ useDraft.call @, replyToMessageId: "local-123", files: []
+ makeComposer.call @
+ expect(@composer.state.enabledFields).toContain Fields.From
+
+ it "enables if requirements are met", ->
+ a1 = new Account()
+ a1.aliases = ['a1']
+ spyOn(AccountStore, 'itemWithId').andCallFake -> a1
+ useDraft.call @, replyToMessageId: null, files: []
+ makeComposer.call @
+ expect(@composer.state.enabledFields).toContain Fields.From
+
+ describe "when enabling fields", ->
+ it "always enables the To and Body fields on empty composers", ->
+ useDraft.apply @
+ makeComposer.call(@)
+ expect(@composer.state.enabledFields).toContain Fields.To
+ expect(@composer.state.enabledFields).toContain Fields.Body
+
+ it "always enables the To and Body fields on full composers", ->
+ useFullDraft.apply(@)
+ makeComposer.call(@)
+ expect(@composer.state.enabledFields).toContain Fields.To
+ expect(@composer.state.enabledFields).toContain Fields.Body
+
+ describe "applying the focused field", ->
+ beforeEach ->
+ useFullDraft.apply(@)
+ makeComposer.call(@)
+ @composer.setState focusedField: Fields.Cc
+ @body = @composer.refs[Fields.Body]
+ spyOn(@body, "focus")
+ spyOn(React, "findDOMNode").andCallThrough()
+ spyOn(@composer, "_applyFieldFocus").andCallThrough()
+
+ it "can focus on the subject", ->
+ @composer.setState focusedField: Fields.Subject
+ expect(@composer._applyFieldFocus.calls.length).toBe 1
+ expect(React.findDOMNode).toHaveBeenCalled()
+ calls = _.filter React.findDOMNode.calls, (call) ->
+ call.args[0].props.name == "subject"
+ expect(calls.length).toBe 1
+
+ it "can focus on the body", ->
+ @composer.setState focusedField: Fields.Body
+ expect(@body.focus).toHaveBeenCalled()
+ expect(@body.focus.calls.length).toBe 1
+
+ it "ignores focuses to participant fields", ->
+ @composer.setState focusedField: Fields.To
+ expect(@body.focus).not.toHaveBeenCalled()
+ expect(@composer._applyFieldFocus.calls.length).toBe 1
+
+ describe "when participants are added during a draft update", ->
+ it "shows the cc fields and bcc fields to ensure participants are never hidden", ->
+ useDraft.call(@, cc: [], bcc: [])
+ makeComposer.call(@)
+ expect(@composer.state.enabledFields).not.toContain Fields.Bcc
+ expect(@composer.state.enabledFields).not.toContain Fields.Cc
+
+ # Simulate a change event fired by the DraftStoreProxy
+ @draft.cc = [u1]
+ @composer._onDraftChanged()
+
+ expect(@composer.state.enabledFields).not.toContain Fields.Bcc
+ expect(@composer.state.enabledFields).toContain Fields.Cc
+
+ # Simulate a change event fired by the DraftStoreProxy
+ @draft.bcc = [u2]
+ @composer._onDraftChanged()
+ expect(@composer.state.enabledFields).toContain Fields.Bcc
+ expect(@composer.state.enabledFields).toContain Fields.Cc
+
+ describe "When sending a message", ->
+ beforeEach ->
+ spyOn(NylasEnv, "isMainWindow").andReturn true
+ remote = require('remote')
+ @dialog = remote.require('dialog')
+ spyOn(remote, "getCurrentWindow")
+ spyOn(@dialog, "showMessageBox")
+ spyOn(Actions, "sendDraft")
+
+ it "shows an error if there are no recipients", ->
+ useDraft.call @, subject: "no recipients"
+ makeComposer.call(@)
+ @composer._sendDraft()
+ expect(Actions.sendDraft).not.toHaveBeenCalled()
+ expect(@dialog.showMessageBox).toHaveBeenCalled()
+ dialogArgs = @dialog.showMessageBox.mostRecentCall.args[1]
+ expect(dialogArgs.detail).toEqual("You need to provide one or more recipients before sending the message.")
+ expect(dialogArgs.buttons).toEqual ['Edit Message']
+
+ it "shows an error if a recipient is invalid", ->
+ useDraft.call @,
+ subject: 'hello world!'
+ to: [new Contact(email: 'lol', name: 'lol')]
+ makeComposer.call(@)
+ @composer._sendDraft()
+ expect(Actions.sendDraft).not.toHaveBeenCalled()
+ expect(@dialog.showMessageBox).toHaveBeenCalled()
+ dialogArgs = @dialog.showMessageBox.mostRecentCall.args[1]
+ expect(dialogArgs.detail).toEqual("lol is not a valid email address - please remove or edit it before sending.")
+ expect(dialogArgs.buttons).toEqual ['Edit Message']
+
+ describe "empty body warning", ->
+ it "warns if the body of the email is still the pristine body", ->
+ pristineBody = "
"
+
+ useDraft.call @,
+ to: [u1]
+ subject: "Hello World"
+ body: pristineBody
+ makeComposer.call(@)
+
+ spyOn(@composer._proxy, 'draftPristineBody').andCallFake -> pristineBody
+
+ @composer._sendDraft()
+ expect(Actions.sendDraft).not.toHaveBeenCalled()
+ expect(@dialog.showMessageBox).toHaveBeenCalled()
+ dialogArgs = @dialog.showMessageBox.mostRecentCall.args[1]
+ expect(dialogArgs.buttons).toEqual ['Send Anyway', 'Cancel']
+
+ it "does not warn if the body of the email is all quoted text, but the email is a forward", ->
+ useDraft.call @,
+ to: [u1]
+ subject: "Fwd: Hello World"
+ body: "This is my quoted text!
"
+ makeComposer.call(@)
+ @composer._sendDraft()
+ expect(Actions.sendDraft).toHaveBeenCalled()
+
+ it "does not warn if the user has attached a file", ->
+ useDraft.call @,
+ to: [u1]
+ subject: "Hello World"
+ body: ""
+ files: [f1]
+ makeComposer.call(@)
+ @composer._sendDraft()
+ expect(Actions.sendDraft).toHaveBeenCalled()
+ expect(@dialog.showMessageBox).not.toHaveBeenCalled()
+
+ it "shows a warning if there's no subject", ->
+ useDraft.call @, to: [u1], subject: ""
+ makeComposer.call(@)
@composer._sendDraft()
expect(Actions.sendDraft).not.toHaveBeenCalled()
expect(@dialog.showMessageBox).toHaveBeenCalled()
dialogArgs = @dialog.showMessageBox.mostRecentCall.args[1]
expect(dialogArgs.buttons).toEqual ['Send Anyway', 'Cancel']
- it "does not warn if the body of the email is all quoted text, but the email is a forward", ->
- useDraft.call @,
- to: [u1]
- subject: "Fwd: Hello World"
- body: "This is my quoted text!
"
- makeComposer.call(@)
+ it "doesn't show a warning if requirements are satisfied", ->
+ useFullDraft.apply(@); makeComposer.call(@)
@composer._sendDraft()
expect(Actions.sendDraft).toHaveBeenCalled()
+ expect(@dialog.showMessageBox).not.toHaveBeenCalled()
- it "does not warn if the user has attached a file", ->
+ describe "Checking for attachments", ->
+ warn = (body) ->
+ useDraft.call @, subject: "Subject", to: [u1], body: body
+ makeComposer.call(@); @composer._sendDraft()
+ expect(Actions.sendDraft).not.toHaveBeenCalled()
+ expect(@dialog.showMessageBox).toHaveBeenCalled()
+ dialogArgs = @dialog.showMessageBox.mostRecentCall.args[1]
+ expect(dialogArgs.buttons).toEqual ['Send Anyway', 'Cancel']
+
+ noWarn = (body) ->
+ useDraft.call @, subject: "Subject", to: [u1], body: body
+ makeComposer.call(@); @composer._sendDraft()
+ expect(Actions.sendDraft).toHaveBeenCalled()
+ expect(@dialog.showMessageBox).not.toHaveBeenCalled()
+
+ it "warns", -> warn.call(@, "Check out the attached file")
+ it "warns", -> warn.call(@, "I've added an attachment")
+ it "warns", -> warn.call(@, "I'm going to attach the file")
+ it "warns", -> warn.call(@, "Hey attach me sup
")
+
+ it "doesn't warn", -> noWarn.call(@, "sup yo")
+ it "doesn't warn", -> noWarn.call(@, "Look at the file")
+ it "doesn't warn", -> noWarn.call(@, "Hey there attach
")
+
+ it "doesn't show a warning if you've attached a file", ->
+ useDraft.call @,
+ subject: "Subject"
+ to: [u1]
+ body: "Check out attached file"
+ files: [f1]
+ makeComposer.call(@); @composer._sendDraft()
+ expect(Actions.sendDraft).toHaveBeenCalled()
+ expect(@dialog.showMessageBox).not.toHaveBeenCalled()
+
+ it "bypasses the warning if force bit is set", ->
+ useDraft.call @, to: [u1], subject: ""
+ makeComposer.call(@)
+ @composer._sendDraft(force: true)
+ expect(Actions.sendDraft).toHaveBeenCalled()
+ expect(@dialog.showMessageBox).not.toHaveBeenCalled()
+
+ it "sends when you click the send button", ->
+ useFullDraft.apply(@); makeComposer.call(@)
+ sendBtn = React.findDOMNode(@composer.refs.sendButton)
+ ReactTestUtils.Simulate.click sendBtn
+ expect(Actions.sendDraft).toHaveBeenCalledWith(DRAFT_CLIENT_ID)
+ expect(Actions.sendDraft.calls.length).toBe 1
+
+ it "doesn't send twice if you double click", ->
+ useFullDraft.apply(@); makeComposer.call(@)
+ sendBtn = React.findDOMNode(@composer.refs.sendButton)
+ ReactTestUtils.Simulate.click sendBtn
+ @isSending = true
+ DraftStore.trigger()
+ ReactTestUtils.Simulate.click sendBtn
+ expect(Actions.sendDraft).toHaveBeenCalledWith(DRAFT_CLIENT_ID)
+ expect(Actions.sendDraft.calls.length).toBe 1
+
+ describe "when sending a message with keyboard inputs", ->
+ beforeEach ->
+ useFullDraft.apply(@)
+ makeComposer.call(@)
+ NylasTestUtils.loadKeymap("internal_packages/composer/keymaps/composer")
+ @$composer = @composer.refs.composerWrap
+
+ it "sends the draft on cmd-enter", ->
+ if process.platform is "darwin"
+ cmdctrl = 'cmd'
+ else
+ cmdctrl = 'ctrl'
+ NylasTestUtils.keyDown("#{cmdctrl}-enter", React.findDOMNode(@$composer))
+ expect(Actions.sendDraft).toHaveBeenCalled()
+ expect(Actions.sendDraft.calls.length).toBe 1
+
+ it "does not send the draft on enter if the button isn't in focus", ->
+ NylasTestUtils.keyDown("enter", React.findDOMNode(@$composer))
+ expect(Actions.sendDraft).not.toHaveBeenCalled()
+
+ it "doesn't let you send twice", ->
+ if process.platform is "darwin"
+ cmdctrl = 'cmd'
+ else
+ cmdctrl = 'ctrl'
+ NylasTestUtils.keyDown("#{cmdctrl}-enter", React.findDOMNode(@$composer))
+ expect(Actions.sendDraft).toHaveBeenCalled()
+ expect(Actions.sendDraft.calls.length).toBe 1
+ @isSending = true
+ DraftStore.trigger()
+ NylasTestUtils.keyDown("#{cmdctrl}-enter", React.findDOMNode(@$composer))
+ expect(Actions.sendDraft).toHaveBeenCalled()
+ expect(Actions.sendDraft.calls.length).toBe 1
+
+ describe "drag and drop", ->
+ beforeEach ->
useDraft.call @,
to: [u1]
subject: "Hello World"
body: ""
files: [f1]
makeComposer.call(@)
- @composer._sendDraft()
- expect(Actions.sendDraft).toHaveBeenCalled()
- expect(@dialog.showMessageBox).not.toHaveBeenCalled()
- it "shows a warning if there's no subject", ->
- useDraft.call @, to: [u1], subject: ""
- makeComposer.call(@)
- @composer._sendDraft()
- expect(Actions.sendDraft).not.toHaveBeenCalled()
- expect(@dialog.showMessageBox).toHaveBeenCalled()
- dialogArgs = @dialog.showMessageBox.mostRecentCall.args[1]
- expect(dialogArgs.buttons).toEqual ['Send Anyway', 'Cancel']
+ describe "_shouldAcceptDrop", ->
+ it "should return true if the event is carrying native files", ->
+ event =
+ dataTransfer:
+ files:[{'pretend':'imafile'}]
+ types:[]
+ expect(@composer._shouldAcceptDrop(event)).toBe(true)
- it "doesn't show a warning if requirements are satisfied", ->
- useFullDraft.apply(@); makeComposer.call(@)
- @composer._sendDraft()
- expect(Actions.sendDraft).toHaveBeenCalled()
- expect(@dialog.showMessageBox).not.toHaveBeenCalled()
+ it "should return true if the event is carrying a non-native file URL not on the draft", ->
+ event =
+ dataTransfer:
+ files:[]
+ types:['text/uri-list']
+ spyOn(@composer, '_nonNativeFilePathForDrop').andReturn("file://one-file")
+ spyOn(FileUploadStore, 'linkedUpload').andReturn({filePath: "file://other-file"})
- describe "Checking for attachments", ->
- warn = (body) ->
- useDraft.call @, subject: "Subject", to: [u1], body: body
- makeComposer.call(@); @composer._sendDraft()
- expect(Actions.sendDraft).not.toHaveBeenCalled()
- expect(@dialog.showMessageBox).toHaveBeenCalled()
- dialogArgs = @dialog.showMessageBox.mostRecentCall.args[1]
- expect(dialogArgs.buttons).toEqual ['Send Anyway', 'Cancel']
+ expect(@composer.state.files.length).toBe(1)
+ expect(@composer._shouldAcceptDrop(event)).toBe(true)
- noWarn = (body) ->
- useDraft.call @, subject: "Subject", to: [u1], body: body
- makeComposer.call(@); @composer._sendDraft()
- expect(Actions.sendDraft).toHaveBeenCalled()
- expect(@dialog.showMessageBox).not.toHaveBeenCalled()
+ it "should return false if the event is carrying a non-native file URL already on the draft", ->
+ event =
+ dataTransfer:
+ files:[]
+ types:['text/uri-list']
+ spyOn(@composer, '_nonNativeFilePathForDrop').andReturn("file://one-file")
+ spyOn(FileUploadStore, 'linkedUpload').andReturn({filePath: "file://one-file"})
- it "warns", -> warn.call(@, "Check out the attached file")
- it "warns", -> warn.call(@, "I've added an attachment")
- it "warns", -> warn.call(@, "I'm going to attach the file")
- it "warns", -> warn.call(@, "Hey attach me sup
")
+ expect(@composer.state.files.length).toBe(1)
+ expect(@composer._shouldAcceptDrop(event)).toBe(false)
- it "doesn't warn", -> noWarn.call(@, "sup yo")
- it "doesn't warn", -> noWarn.call(@, "Look at the file")
- it "doesn't warn", -> noWarn.call(@, "Hey there attach
")
+ it "should return false otherwise", ->
+ event =
+ dataTransfer:
+ files:[]
+ types:['text/plain']
+ expect(@composer._shouldAcceptDrop(event)).toBe(false)
- it "doesn't show a warning if you've attached a file", ->
- useDraft.call @,
- subject: "Subject"
- to: [u1]
- body: "Check out attached file"
- files: [f1]
- makeComposer.call(@); @composer._sendDraft()
- expect(Actions.sendDraft).toHaveBeenCalled()
- expect(@dialog.showMessageBox).not.toHaveBeenCalled()
+ describe "_nonNativeFilePathForDrop", ->
+ it "should return a path in the text/nylas-file-url data", ->
+ event =
+ dataTransfer:
+ types: ['text/nylas-file-url']
+ getData: -> "image/png:test.png:file:///Users/bengotow/Desktop/test.png"
+ expect(@composer._nonNativeFilePathForDrop(event)).toBe("/Users/bengotow/Desktop/test.png")
- it "bypasses the warning if force bit is set", ->
- useDraft.call @, to: [u1], subject: ""
- makeComposer.call(@)
- @composer._sendDraft(force: true)
- expect(Actions.sendDraft).toHaveBeenCalled()
- expect(@dialog.showMessageBox).not.toHaveBeenCalled()
+ it "should return a path in the text/uri-list data", ->
+ event =
+ dataTransfer:
+ types: ['text/uri-list']
+ getData: -> "file:///Users/bengotow/Desktop/test.png"
+ expect(@composer._nonNativeFilePathForDrop(event)).toBe("/Users/bengotow/Desktop/test.png")
- it "sends when you click the send button", ->
- useFullDraft.apply(@); makeComposer.call(@)
- sendBtn = React.findDOMNode(@composer.refs.sendButton)
- ReactTestUtils.Simulate.click sendBtn
- expect(Actions.sendDraft).toHaveBeenCalledWith(DRAFT_CLIENT_ID)
- expect(Actions.sendDraft.calls.length).toBe 1
+ it "should return null otherwise", ->
+ event =
+ dataTransfer:
+ types: ['text/plain']
+ getData: -> "Hello world"
+ expect(@composer._nonNativeFilePathForDrop(event)).toBe(null)
- it "doesn't send twice if you double click", ->
- useFullDraft.apply(@); makeComposer.call(@)
- sendBtn = React.findDOMNode(@composer.refs.sendButton)
- ReactTestUtils.Simulate.click sendBtn
- @isSending = true
- DraftStore.trigger()
- ReactTestUtils.Simulate.click sendBtn
- expect(Actions.sendDraft).toHaveBeenCalledWith(DRAFT_CLIENT_ID)
- expect(Actions.sendDraft.calls.length).toBe 1
+ it "should urldecode the contents of the text/uri-list field", ->
+ event =
+ dataTransfer:
+ types: ['text/uri-list']
+ getData: -> "file:///Users/bengotow/Desktop/Screen%20shot.png"
+ expect(@composer._nonNativeFilePathForDrop(event)).toBe("/Users/bengotow/Desktop/Screen shot.png")
- describe "when sending a message with keyboard inputs", ->
+ it "should return null if text/uri-list contains a non-file path", ->
+ event =
+ dataTransfer:
+ types: ['text/uri-list']
+ getData: -> "http://apple.com"
+ expect(@composer._nonNativeFilePathForDrop(event)).toBe(null)
+
+ it "should return null if text/nylas-file-url contains a non-file path", ->
+ event =
+ dataTransfer:
+ types: ['text/nylas-file-url']
+ getData: -> "application/json:filename.json:undefined"
+ expect(@composer._nonNativeFilePathForDrop(event)).toBe(null)
+
+ describe "when scrolling to track your cursor", ->
+ it "it tracks when you're at the end of the text", ->
+
+ it "it doesn't track when typing in the middle of the body", ->
+
+ it "it doesn't track when typing in the middle of the body", ->
+
+ describe "When composing a new message", ->
+ it "Can add someone in the to field", ->
+
+ it "Can add someone in the cc field", ->
+
+ it "Can add someone in the bcc field", ->
+
+ describe "When replying to a message", ->
+
+ describe "When replying all to a message", ->
+
+ describe "When forwarding a message", ->
+
+ describe "When changing the subject of a message", ->
+
+ describe "A draft with files (attachments) and uploads", ->
beforeEach ->
- useFullDraft.apply(@)
- makeComposer.call(@)
- NylasTestUtils.loadKeymap("internal_packages/composer/keymaps/composer")
- @$composer = @composer.refs.composerWrap
+ @file1 = new File
+ id: "f_1"
+ filename: "f1.pdf"
+ size: 1230
- it "sends the draft on cmd-enter", ->
- if process.platform is "darwin"
- cmdctrl = 'cmd'
- else
- cmdctrl = 'ctrl'
- NylasTestUtils.keyDown("#{cmdctrl}-enter", React.findDOMNode(@$composer))
- expect(Actions.sendDraft).toHaveBeenCalled()
- expect(Actions.sendDraft.calls.length).toBe 1
+ @file2 = new File
+ id: "f_2"
+ filename: "f2.jpg"
+ size: 4560
- it "does not send the draft on enter if the button isn't in focus", ->
- NylasTestUtils.keyDown("enter", React.findDOMNode(@$composer))
- expect(Actions.sendDraft).not.toHaveBeenCalled()
+ @file3 = new File
+ id: "f_3"
+ filename: "f3.png"
+ size: 7890
- it "doesn't let you send twice", ->
- if process.platform is "darwin"
- cmdctrl = 'cmd'
- else
- cmdctrl = 'ctrl'
- NylasTestUtils.keyDown("#{cmdctrl}-enter", React.findDOMNode(@$composer))
- expect(Actions.sendDraft).toHaveBeenCalled()
- expect(Actions.sendDraft.calls.length).toBe 1
- @isSending = true
- DraftStore.trigger()
- NylasTestUtils.keyDown("#{cmdctrl}-enter", React.findDOMNode(@$composer))
- expect(Actions.sendDraft).toHaveBeenCalled()
- expect(Actions.sendDraft.calls.length).toBe 1
+ @up1 =
+ uploadTaskId: 4
+ messageClientId: DRAFT_CLIENT_ID
+ filePath: "/foo/bar/f4.bmp"
+ fileName: "f4.bmp"
+ fileSize: 1024
- describe "drag and drop", ->
+ @up2 =
+ uploadTaskId: 5
+ messageClientId: DRAFT_CLIENT_ID
+ filePath: "/foo/bar/f5.zip"
+ fileName: "f5.zip"
+ fileSize: 1024
+
+ spyOn(Actions, "fetchFile")
+ spyOn(FileUploadStore, "linkedUpload").andReturn null
+ spyOn(FileUploadStore, "uploadsForMessage").andReturn [@up1, @up2]
+
+ useDraft.call @, files: [@file1, @file2]
+ makeComposer.call @
+
+ it 'starts fetching attached files', ->
+ waitsFor ->
+ Actions.fetchFile.callCount == 1
+ runs ->
+ expect(Actions.fetchFile).toHaveBeenCalled()
+ expect(Actions.fetchFile.calls.length).toBe(1)
+ expect(Actions.fetchFile.calls[0].args[0]).toBe @file2
+
+ it 'injects an Attachment component for non image files', ->
+ els = ReactTestUtils.scryRenderedComponentsWithTypeAndProps(@composer, InjectedComponent, matching: {role: "Attachment"})
+ expect(els.length).toBe 1
+
+ it 'injects an Attachment:Image component for image files', ->
+ els = ReactTestUtils.scryRenderedComponentsWithTypeAndProps(@composer, InjectedComponent, matching: {role: "Attachment:Image"})
+ expect(els.length).toBe 1
+
+ describe "when the DraftStore `isSending` isn't stubbed out", ->
beforeEach ->
- useDraft.call @,
- to: [u1]
- subject: "Hello World"
- body: ""
- files: [f1]
+ DraftStore._draftsSending = {}
+
+ it "doesn't send twice in a popout", ->
+ spyOn(Actions, "queueTask")
+ spyOn(Actions, "sendDraft").andCallThrough()
+ useFullDraft.call(@)
makeComposer.call(@)
+ @composer._sendDraft()
+ @composer._sendDraft()
+ expect(Actions.sendDraft.calls.length).toBe 1
- describe "_shouldAcceptDrop", ->
- it "should return true if the event is carrying native files", ->
- event =
- dataTransfer:
- files:[{'pretend':'imafile'}]
- types:[]
- expect(@composer._shouldAcceptDrop(event)).toBe(true)
-
- it "should return true if the event is carrying a non-native file URL not on the draft", ->
- event =
- dataTransfer:
- files:[]
- types:['text/uri-list']
- spyOn(@composer, '_nonNativeFilePathForDrop').andReturn("file://one-file")
- spyOn(FileUploadStore, 'linkedUpload').andReturn({filePath: "file://other-file"})
-
- expect(@composer.state.files.length).toBe(1)
- expect(@composer._shouldAcceptDrop(event)).toBe(true)
-
- it "should return false if the event is carrying a non-native file URL already on the draft", ->
- event =
- dataTransfer:
- files:[]
- types:['text/uri-list']
- spyOn(@composer, '_nonNativeFilePathForDrop').andReturn("file://one-file")
- spyOn(FileUploadStore, 'linkedUpload').andReturn({filePath: "file://one-file"})
-
- expect(@composer.state.files.length).toBe(1)
- expect(@composer._shouldAcceptDrop(event)).toBe(false)
-
- it "should return false otherwise", ->
- event =
- dataTransfer:
- files:[]
- types:['text/plain']
- expect(@composer._shouldAcceptDrop(event)).toBe(false)
-
- describe "_nonNativeFilePathForDrop", ->
- it "should return a path in the text/nylas-file-url data", ->
- event =
- dataTransfer:
- types: ['text/nylas-file-url']
- getData: -> "image/png:test.png:file:///Users/bengotow/Desktop/test.png"
- expect(@composer._nonNativeFilePathForDrop(event)).toBe("/Users/bengotow/Desktop/test.png")
-
- it "should return a path in the text/uri-list data", ->
- event =
- dataTransfer:
- types: ['text/uri-list']
- getData: -> "file:///Users/bengotow/Desktop/test.png"
- expect(@composer._nonNativeFilePathForDrop(event)).toBe("/Users/bengotow/Desktop/test.png")
-
- it "should return null otherwise", ->
- event =
- dataTransfer:
- types: ['text/plain']
- getData: -> "Hello world"
- expect(@composer._nonNativeFilePathForDrop(event)).toBe(null)
-
- it "should urldecode the contents of the text/uri-list field", ->
- event =
- dataTransfer:
- types: ['text/uri-list']
- getData: -> "file:///Users/bengotow/Desktop/Screen%20shot.png"
- expect(@composer._nonNativeFilePathForDrop(event)).toBe("/Users/bengotow/Desktop/Screen shot.png")
-
- it "should return null if text/uri-list contains a non-file path", ->
- event =
- dataTransfer:
- types: ['text/uri-list']
- getData: -> "http://apple.com"
- expect(@composer._nonNativeFilePathForDrop(event)).toBe(null)
-
- it "should return null if text/nylas-file-url contains a non-file path", ->
- event =
- dataTransfer:
- types: ['text/nylas-file-url']
- getData: -> "application/json:filename.json:undefined"
- expect(@composer._nonNativeFilePathForDrop(event)).toBe(null)
-
- describe "when scrolling to track your cursor", ->
- it "it tracks when you're at the end of the text", ->
-
- it "it doesn't track when typing in the middle of the body", ->
-
- it "it doesn't track when typing in the middle of the body", ->
-
- describe "When composing a new message", ->
- it "Can add someone in the to field", ->
-
- it "Can add someone in the cc field", ->
-
- it "Can add someone in the bcc field", ->
-
- describe "When replying to a message", ->
-
- describe "When replying all to a message", ->
-
- describe "When forwarding a message", ->
-
- describe "When changing the subject of a message", ->
-
- describe "A draft with files (attachments) and uploads", ->
- beforeEach ->
- @file1 = new File
- id: "f_1"
- filename: "f1.pdf"
- size: 1230
-
- @file2 = new File
- id: "f_2"
- filename: "f2.jpg"
- size: 4560
-
- @file3 = new File
- id: "f_3"
- filename: "f3.png"
- size: 7890
-
- @up1 =
- uploadTaskId: 4
- messageClientId: DRAFT_CLIENT_ID
- filePath: "/foo/bar/f4.bmp"
- fileName: "f4.bmp"
- fileSize: 1024
-
- @up2 =
- uploadTaskId: 5
- messageClientId: DRAFT_CLIENT_ID
- filePath: "/foo/bar/f5.zip"
- fileName: "f5.zip"
- fileSize: 1024
-
- spyOn(Actions, "fetchFile")
- spyOn(FileUploadStore, "linkedUpload").andReturn null
- spyOn(FileUploadStore, "uploadsForMessage").andReturn [@up1, @up2]
-
- useDraft.call @, files: [@file1, @file2]
- makeComposer.call @
-
- it 'starts fetching attached files', ->
- waitsFor ->
- Actions.fetchFile.callCount == 1
- runs ->
- expect(Actions.fetchFile).toHaveBeenCalled()
- expect(Actions.fetchFile.calls.length).toBe(1)
- expect(Actions.fetchFile.calls[0].args[0]).toBe @file2
-
- it 'injects an Attachment component for non image files', ->
- els = ReactTestUtils.scryRenderedComponentsWithTypeAndProps(@composer, InjectedComponent, matching: {role: "Attachment"})
- expect(els.length).toBe 1
-
- it 'injects an Attachment:Image component for image files', ->
- els = ReactTestUtils.scryRenderedComponentsWithTypeAndProps(@composer, InjectedComponent, matching: {role: "Attachment:Image"})
- expect(els.length).toBe 1
-
-describe "when the DraftStore `isSending` isn't stubbed out", ->
- beforeEach ->
- DraftStore._draftsSending = {}
-
- it "doesn't send twice in a popout", ->
- spyOn(Actions, "queueTask")
- spyOn(Actions, "sendDraft").andCallThrough()
- useFullDraft.call(@)
- makeComposer.call(@)
- @composer._sendDraft()
- @composer._sendDraft()
- expect(Actions.sendDraft.calls.length).toBe 1
-
- it "doesn't send twice in the main window", ->
- spyOn(Actions, "queueTask")
- spyOn(Actions, "sendDraft").andCallThrough()
- spyOn(NylasEnv, "isMainWindow").andReturn true
- useFullDraft.call(@)
- makeComposer.call(@)
- @composer._sendDraft()
- @composer._sendDraft()
- expect(Actions.sendDraft.calls.length).toBe 1
+ it "doesn't send twice in the main window", ->
+ spyOn(Actions, "queueTask")
+ spyOn(Actions, "sendDraft").andCallThrough()
+ spyOn(NylasEnv, "isMainWindow").andReturn true
+ useFullDraft.call(@)
+ makeComposer.call(@)
+ @composer._sendDraft()
+ @composer._sendDraft()
+ expect(Actions.sendDraft.calls.length).toBe 1
diff --git a/internal_packages/composer/spec/quoted-text-spec.cjsx b/internal_packages/composer/spec/quoted-text-spec.cjsx
index 768f022ad..3a635766e 100644
--- a/internal_packages/composer/spec/quoted-text-spec.cjsx
+++ b/internal_packages/composer/spec/quoted-text-spec.cjsx
@@ -9,10 +9,18 @@ ReactTestUtils = React.addons.TestUtils
Fields = require '../lib/fields'
Composer = require "../lib/composer-view"
-{DraftStore} = require 'nylas-exports'
+ComposerEditor = require '../lib/composer-editor'
+
+{DraftStore, ComponentRegistry} = require 'nylas-exports'
describe "Composer Quoted Text", ->
beforeEach ->
+ # TODO
+ # Extract ComposerEditor tests instead of rendering injected component
+ # here
+ ComposerEditor.containerRequired = false
+ ComponentRegistry.register(ComposerEditor, role: "Composer:Editor")
+
@onChange = jasmine.createSpy('onChange')
@htmlNoQuote = 'Test HTML
'
@htmlWithQuote = 'Test HTMLQUOTE
'
@@ -28,6 +36,8 @@ describe "Composer Quoted Text", ->
afterEach ->
DraftStore._cleanupAllSessions()
+ ComposerEditor.containerRequired = undefined
+ ComponentRegistry.unregister(ComposerEditor)
# Must be called with the test's scope
setHTML = (newHTML) ->
diff --git a/internal_packages/message-list/lib/message-item-container.cjsx b/internal_packages/message-list/lib/message-item-container.cjsx
index b6eb65089..94c1b7815 100644
--- a/internal_packages/message-list/lib/message-item-container.cjsx
+++ b/internal_packages/message-list/lib/message-item-container.cjsx
@@ -18,7 +18,7 @@ class MessageItemContainer extends React.Component
collapsed: React.PropTypes.bool
isLastMsg: React.PropTypes.bool
isBeforeReplyArea: React.PropTypes.bool
- onRequestScrollTo: React.PropTypes.func
+ scrollTo: React.PropTypes.func
constructor: (@props) ->
@state = @_getStateFromStores()
@@ -62,7 +62,7 @@ class MessageItemContainer extends React.Component
mode: "inline"
draftClientId: @props.message.clientId
threadId: @props.thread.id
- onRequestScrollTo: @props.onRequestScrollTo
+ scrollTo: @props.scrollTo
""" - it 'works with these manual test cases', -> for {before, after} in tests opts = keepIfWholeBodyIsQuote: true @@ -273,6 +272,26 @@ describe "QuotedHTMLTransformer", -> expect0 = "
"
expect(QuotedHTMLTransformer.removeQuotedHTML(input0)).toEqual expect0
+ it 'works as expected when body tag inside the html', ->
+ input0 = """
+
+ On Dec 16 2015, at 7:08 pm, Juan Tejada <juan@nylas.com> wrote: +
+ + + + ++ + +h2
+he he hehehehehehe
+dufjcasc
+
+ """ + expect0 = "
" + expect(QuotedHTMLTransformer.removeQuotedHTML(input0)).toEqual expect0 # We have a little utility method that you can manually uncomment to diff --git a/src/components/contenteditable/contenteditable.cjsx b/src/components/contenteditable/contenteditable.cjsx index 3e409925e..2fa232c49 100644 --- a/src/components/contenteditable/contenteditable.cjsx +++ b/src/components/contenteditable/contenteditable.cjsx @@ -35,13 +35,11 @@ class Contenteditable extends React.Component # The current html state, as a string, of the contenteditable. value: React.PropTypes.string - initialSelectionSnapshot: React.PropTypes.object + # Initial content selection that was previously saved + initialSelectionSnapshot: React.PropTypes.object, # Handlers onChange: React.PropTypes.func.isRequired - # Passes an absolute top coordinate to scroll to. - onScrollTo: React.PropTypes.func - onScrollToBottom: React.PropTypes.func onFilePaste: React.PropTypes.func # A list of objects that extend {ContenteditableExtension} @@ -549,7 +547,7 @@ class Contenteditable extends React.Component endNodeIndex: DOMUtils.getNodeIndex(context, selection.focusNode) isCollapsed: selection.isCollapsed - @_ensureSelectionVisible(selection) + @_onSelectionChanged(selection) @setInnerState selection: @_selection @@ -564,54 +562,11 @@ class Contenteditable extends React.Component selection: @_selection editableFocused: true - # When the selectionState gets set by a parent (e.g. undo-ing and - # redo-ing) we need to make sure it's visible to the user. - # - # Unfortunately, we can't use the native `scrollIntoView` because it - # naively scrolls the whole window and doesn't know not to scroll if - # it's already in view. There's a new native method called - # `scrollIntoViewIfNeeded`, but this only works when the scroll - # container is a direct parent of the requested element. In this case - # the scroll container may be many levels up. - _ensureSelectionVisible: (selection) -> - # If our parent supports scroll to bottom, check for that - if @_shouldScrollToBottom(selection) - @props.onScrollToBottom() - - # Don't bother computing client rects if no scroll method has been provided - else if @props.onScrollTo - rangeInScope = DOMUtils.getRangeInScope(@_editableNode()) - return unless rangeInScope - - rect = rangeInScope.getBoundingClientRect() - if DOMUtils.isEmptyBoudingRect(rect) - rect = DOMUtils.getSelectionRectFromDOM(selection) - - if rect - @props.onScrollTo({rect}) - + _onSelectionChanged: (selection) -> + @props.onSelectionChanged(selection, @_editableNode()) # The bounding client rect has changed @setInnerState editableNode: @_editableNode() - # As you're typing a lot of content and the cursor begins to scroll off - # to the bottom, we want to make it look like we're tracking your - # typing. - _shouldScrollToBottom: (selection) -> - (@props.onScrollToBottom and - DOMUtils.atEndOfContent(selection, @_editableNode()) and - @_bottomIsNearby()) - - # If the bottom of the container we're scrolling to is really far away - # from this contenteditable and your scroll position, we don't want to - # jump away. This can commonly happen if the composer has a very tall - # image attachment. The "send" button may be 1000px away from the bottom - # of the contenteditable. props.onScrollToBottom moves to the bottom of - # the "send" button. - _bottomIsNearby: -> - parentRect = @props.getComposerBoundingRect() - selfRect = @_editableNode().getBoundingClientRect() - return Math.abs(parentRect.bottom - selfRect.bottom) <= 250 - # We use global listeners to determine whether or not dragging is # happening. This is because dragging may stop outside the scope of # this element. Note that the `dragstart` and `dragend` events don't @@ -741,7 +696,7 @@ class Contenteditable extends React.Component newEndNode, @_selection.endOffset) - @_ensureSelectionVisible(selection) + @_onSelectionChanged(selection) @_setupListeners() # This needs to be in the contenteditable area because we need to first diff --git a/src/components/injected-component-set.cjsx b/src/components/injected-component-set.cjsx index 7710fb83e..86ee538ab 100644 --- a/src/components/injected-component-set.cjsx +++ b/src/components/injected-component-set.cjsx @@ -57,6 +57,7 @@ class InjectedComponentSet extends React.Component className: React.PropTypes.string exposedProps: React.PropTypes.object containersRequired: React.PropTypes.bool + requiredMethods: React.PropTypes.arrayOf(React.PropTypes.string) @defaultProps: direction: 'row' diff --git a/src/components/injected-component.cjsx b/src/components/injected-component.cjsx index 49343419e..84a3e90a5 100644 --- a/src/components/injected-component.cjsx +++ b/src/components/injected-component.cjsx @@ -41,14 +41,28 @@ class InjectedComponent extends React.Component - `exposedProps` (optional) An {Object} with props that will be passed to each item rendered into the set. + - `requiredMethods` (options) An {Array} with a list of methods that should be + implemented by the registered component instance. If these are not implemented, + an error will be thrown. + + - `fallback` (optional) A {Component} to default to in case there are no matching + components in the ComponentRegistry + ### @propTypes: matching: React.PropTypes.object.isRequired className: React.PropTypes.string exposedProps: React.PropTypes.object + fallback: React.PropTypes.func + requiredMethods: React.PropTypes.arrayOf(React.PropTypes.string) + + @defaultProps: + requiredMethods: [] constructor: (@props) -> @state = @_getStateFromStores() + @_verifyRequiredMethods() + @_setRequiredMethods(@props.requiredMethods) componentDidMount: => @_componentUnlistener = ComponentRegistry.listen => @@ -61,6 +75,9 @@ class InjectedComponent extends React.Component if not _.isEqual(newProps.matching, @props?.matching) @setState(@_getStateFromStores(newProps)) + componentDidUpdate: => + @_setRequiredMethods(@props.requiredMethods) + render: => return
unless @state.component
@@ -69,7 +86,6 @@ class InjectedComponent extends React.Component
className += " registered-region-visible" if @state.visible
component = @state.component
-
if component.containerRequired is false
element =