Mailspring/internal_packages/message-list/spec/message-list-spec.cjsx
Ben Gotow 4b8dd87410 fix(T2049): in 2-pane mode, keyboard actions are applied to selection, not message you are viewing
Fixes T2049 and moves ownership of the reply/reply-all/forward actions to the MessageList, which Rob did for starring and actually makes more sense.
2015-06-26 14:47:14 -07:00

441 lines
13 KiB
CoffeeScript

_ = require "underscore"
moment = require "moment"
proxyquire = require "proxyquire"
CSON = require "season"
React = require "react/addons"
TestUtils = React.addons.TestUtils
{Thread,
Contact,
Actions,
Message,
Namespace,
MessageStore,
NamespaceStore,
NylasTestUtils,
ComponentRegistry} = require "nylas-exports"
{InjectedComponent} = require 'nylas-component-kit'
MessageItem = proxyquire("../lib/message-item", {
"./email-frame": React.createClass({render: -> <div></div>})
})
MessageList = proxyquire("../lib/message-list", {
"./message-item": MessageItem
})
MessageParticipants = require "../lib/message-participants"
me = new Namespace(
"name": "User One",
"email": "user1@nylas.com"
"provider": "inbox"
)
NamespaceStore._current = me
user_headers =
id: null
object: null
namespace_id: null
user_1 = _.extend _.clone(user_headers),
name: "User One"
email: "user1@nylas.com"
user_2 = _.extend _.clone(user_headers),
name: "User Two"
email: "user2@nylas.com"
user_3 = _.extend _.clone(user_headers),
name: "User Three"
email: "user3@nylas.com"
user_4 = _.extend _.clone(user_headers),
name: "User Four"
email: "user4@nylas.com"
user_5 = _.extend _.clone(user_headers),
name: "User Five"
email: "user5@nylas.com"
m1 = (new Message).fromJSON({
"id" : "111",
"from" : [ user_1 ],
"to" : [ user_2 ],
"cc" : [ user_3, user_4 ],
"bcc" : null,
"body" : "Body One",
"date" : 1415814587,
"draft" : false
"files" : [],
"unread" : false,
"object" : "message",
"snippet" : "snippet one...",
"subject" : "Subject One",
"thread_id" : "thread_12345",
"namespace_id" : "nsid"
})
m2 = (new Message).fromJSON({
"id" : "222",
"from" : [ user_2 ],
"to" : [ user_1 ],
"cc" : [ user_3, user_4 ],
"bcc" : null,
"body" : "Body Two",
"date" : 1415814587,
"draft" : false
"files" : [],
"unread" : false,
"object" : "message",
"snippet" : "snippet Two...",
"subject" : "Subject Two",
"thread_id" : "thread_12345",
"namespace_id" : "nsid"
})
m3 = (new Message).fromJSON({
"id" : "333",
"from" : [ user_3 ],
"to" : [ user_1 ],
"cc" : [ user_2, user_4 ],
"bcc" : [],
"body" : "Body Three",
"date" : 1415814587,
"draft" : false
"files" : [],
"unread" : false,
"object" : "message",
"snippet" : "snippet Three...",
"subject" : "Subject Three",
"thread_id" : "thread_12345",
"namespace_id" : "nsid"
})
m4 = (new Message).fromJSON({
"id" : "444",
"from" : [ user_4 ],
"to" : [ user_1 ],
"cc" : [],
"bcc" : [ user_5 ],
"body" : "Body Four",
"date" : 1415814587,
"draft" : false
"files" : [],
"unread" : false,
"object" : "message",
"snippet" : "snippet Four...",
"subject" : "Subject Four",
"thread_id" : "thread_12345",
"namespace_id" : "nsid"
})
m5 = (new Message).fromJSON({
"id" : "555",
"from" : [ user_1 ],
"to" : [ user_4 ],
"cc" : [],
"bcc" : [],
"body" : "Body Five",
"date" : 1415814587,
"draft" : false
"files" : [],
"unread" : false,
"object" : "message",
"snippet" : "snippet Five...",
"subject" : "Subject Five",
"thread_id" : "thread_12345",
"namespace_id" : "nsid"
})
testMessages = [m1, m2, m3, m4, m5]
draftMessages = [
(new Message).fromJSON({
"id" : "666",
"from" : [ user_1 ],
"to" : [ ],
"cc" : [ ],
"bcc" : null,
"body" : "Body One",
"date" : 1415814587,
"draft" : true
"files" : [],
"unread" : false,
"object" : "draft",
"snippet" : "draft snippet one...",
"subject" : "Draft One",
"thread_id" : "thread_12345",
"namespace_id" : "nsid"
}),
]
test_thread = (new Thread).fromJSON({
"id" : "thread_12345"
"subject" : "Subject 12345"
})
describe "MessageList", ->
beforeEach ->
MessageStore._items = []
MessageStore._threadId = null
spyOn(MessageStore, "itemLocalIds").andCallFake ->
{"666": "666"}
spyOn(MessageStore, "itemsLoading").andCallFake ->
false
@messageList = TestUtils.renderIntoDocument(<MessageList />)
@messageList_node = React.findDOMNode(@messageList)
it "renders into the document", ->
expect(TestUtils.isCompositeComponentWithType(@messageList,
MessageList)).toBe true
it "by default has zero children", ->
items = TestUtils.scryRenderedComponentsWithType(@messageList,
MessageItem)
expect(items.length).toBe 0
describe "Populated Message list", ->
beforeEach ->
MessageStore._items = testMessages
MessageStore._expandItemsToDefault()
MessageStore.trigger(MessageStore)
@messageList.setState currentThread: test_thread
NylasTestUtils.loadKeymap("keymaps/base")
it "renders all the correct number of messages", ->
items = TestUtils.scryRenderedComponentsWithType(@messageList,
MessageItem)
expect(items.length).toBe 5
it "renders the correct number of expanded messages", ->
msgs = TestUtils.scryRenderedDOMComponentsWithClass(@messageList, "message-item-wrap collapsed")
expect(msgs.length).toBe 4
it "aggregates participants across all messages", ->
expect(@messageList._threadParticipants().length).toBe 4
expect(@messageList._threadParticipants()[0] instanceof Contact).toBe true
it "displays lists of participants on the page", ->
items = TestUtils.scryRenderedComponentsWithType(@messageList,
MessageParticipants)
expect(items.length).toBe 1
it "toggles star on a thread if 's' is pressed", ->
spyOn(@messageList, "_onStar")
NylasTestUtils.keyPress("s", document.body)
expect(@messageList._onStar).toHaveBeenCalled()
it "focuses new composers when a draft is added", ->
spyOn(@messageList, "_focusDraft")
msgs = @messageList.state.messages
@messageList.setState
messages: msgs.concat(draftMessages)
expect(@messageList._focusDraft).toHaveBeenCalled()
expect(@messageList._focusDraft.mostRecentCall.args[0].props.exposedProps.localId).toEqual(draftMessages[0].id)
describe "MessageList with draft", ->
beforeEach ->
MessageStore._items = testMessages.concat draftMessages
MessageStore.trigger(MessageStore)
spyOn(@messageList, "_focusDraft")
@messageList.setState(currentThread: test_thread)
it "renders the composer", ->
items = TestUtils.scryRenderedComponentsWithTypeAndProps(@messageList, InjectedComponent, matching: {role:"Composer"})
expect(@messageList.state.messages.length).toBe 6
expect(items.length).toBe 1
it "doesn't focus on initial load", ->
expect(@messageList._focusDraft).not.toHaveBeenCalled()
describe "reply type", ->
it "prompts for a reply when there's only one participant", ->
MessageStore._items = [m3, m5]
MessageStore.trigger()
@messageList.setState currentThread: test_thread
expect(@messageList._replyType()).toBe "reply"
cs = TestUtils.scryRenderedDOMComponentsWithClass(@messageList, "footer-reply-area")
expect(cs.length).toBe 1
it "prompts for a reply-all when there's more then one participant", ->
MessageStore._items = [m5, m3]
MessageStore.trigger()
@messageList.setState currentThread: test_thread
expect(@messageList._replyType()).toBe "reply-all"
cs = TestUtils.scryRenderedDOMComponentsWithClass(@messageList, "footer-reply-area")
expect(cs.length).toBe 1
it "hides the reply type if the last message is a draft", ->
MessageStore._items = [m5, m3, draftMessages[0]]
MessageStore.trigger()
@messageList.setState currentThread: test_thread
cs = TestUtils.scryRenderedDOMComponentsWithClass(@messageList, "footer-reply-area")
expect(cs.length).toBe 0
describe "Message minification", ->
beforeEach ->
@messageList.MINIFY_THRESHOLD = 3
@messageList.setState minified: true
@messages = [
{id: 'a'}, {id: 'b'}, {id: 'c'}, {id: 'd'}, {id: 'e'}, {id: 'f'}, {id: 'g'}
]
it "ignores the first message if it's collapsed", ->
@messageList.setState messagesExpandedState:
a: false, b: false, c: false, d: false, e: false, f: false, g: "default"
out = @messageList._messagesWithMinification(@messages)
expect(out).toEqual [
{id: 'a'},
{
type: "minifiedBundle"
messages: [{id: 'b'}, {id: 'c'}, {id: 'd'}, {id: 'e'}]
},
{id: 'f'},
{id: 'g'}
]
it "ignores the first message if it's expanded", ->
@messageList.setState messagesExpandedState:
a: "default", b: false, c: false, d: false, e: false, f: false, g: "default"
out = @messageList._messagesWithMinification(@messages)
expect(out).toEqual [
{id: 'a'},
{
type: "minifiedBundle"
messages: [{id: 'b'}, {id: 'c'}, {id: 'd'}, {id: 'e'}]
},
{id: 'f'},
{id: 'g'}
]
it "doesn't minify the last collapsed message", ->
@messageList.setState messagesExpandedState:
a: false, b: false, c: false, d: false, e: false, f: "default", g: "default"
out = @messageList._messagesWithMinification(@messages)
expect(out).toEqual [
{id: 'a'},
{
type: "minifiedBundle"
messages: [{id: 'b'}, {id: 'c'}, {id: 'd'}]
},
{id: 'e'},
{id: 'f'},
{id: 'g'}
]
it "allows explicitly expanded messages", ->
@messageList.setState messagesExpandedState:
a: false, b: false, c: false, d: false, e: false, f: "explicit", g: "default"
out = @messageList._messagesWithMinification(@messages)
expect(out).toEqual [
{id: 'a'},
{
type: "minifiedBundle"
messages: [{id: 'b'}, {id: 'c'}, {id: 'd'}, {id: 'e'}]
},
{id: 'f'},
{id: 'g'}
]
it "doesn't minify if the threshold isn't reached", ->
@messageList.setState messagesExpandedState:
a: false, b: "default", c: false, d: "default", e: false, f: "default", g: "default"
out = @messageList._messagesWithMinification(@messages)
expect(out).toEqual [
{id: 'a'},
{id: 'b'},
{id: 'c'},
{id: 'd'},
{id: 'e'},
{id: 'f'},
{id: 'g'}
]
it "doesn't minify if the threshold isn't reached due to the rule about not minifying the last collapsed messages", ->
@messageList.setState messagesExpandedState:
a: false, b: false, c: false, d: false, e: "default", f: "default", g: "default"
out = @messageList._messagesWithMinification(@messages)
expect(out).toEqual [
{id: 'a'},
{id: 'b'},
{id: 'c'},
{id: 'd'},
{id: 'e'},
{id: 'f'},
{id: 'g'}
]
it "minifies at the threshold if the message is explicitly expanded", ->
@messageList.setState messagesExpandedState:
a: false, b: false, c: false, d: false, e: "explicit", f: "default", g: "default"
out = @messageList._messagesWithMinification(@messages)
expect(out).toEqual [
{id: 'a'},
{
type: "minifiedBundle"
messages: [{id: 'b'}, {id: 'c'}, {id: 'd'}]
},
{id: 'e'},
{id: 'f'},
{id: 'g'}
]
it "can have multiple minification blocks", ->
messages = [
{id: 'a'}, {id: 'b'}, {id: 'c'}, {id: 'd'}, {id: 'e'}, {id: 'f'},
{id: 'g'}, {id: 'h'}, {id: 'i'}, {id: 'j'}, {id: 'k'}, {id: 'l'}
]
@messageList.setState messagesExpandedState:
a: false, b: false, c: false, d: false, e: false, f: "default",
g: false, h: false, i: false, j: false, k: false, l: "default"
out = @messageList._messagesWithMinification(messages)
expect(out).toEqual [
{id: 'a'},
{
type: "minifiedBundle"
messages: [{id: 'b'}, {id: 'c'}, {id: 'd'}]
},
{id: 'e'},
{id: 'f'},
{
type: "minifiedBundle"
messages: [{id: 'g'}, {id: 'h'}, {id: 'i'}, {id: 'j'}]
},
{id: 'k'},
{id: 'l'}
]
it "can have multiple minification blocks next to explicitly expanded messages", ->
messages = [
{id: 'a'}, {id: 'b'}, {id: 'c'}, {id: 'd'}, {id: 'e'}, {id: 'f'},
{id: 'g'}, {id: 'h'}, {id: 'i'}, {id: 'j'}, {id: 'k'}, {id: 'l'}
]
@messageList.setState messagesExpandedState:
a: false, b: false, c: false, d: false, e: "explicit", f: "default",
g: false, h: false, i: false, j: false, k: "explicit", l: "default"
out = @messageList._messagesWithMinification(messages)
expect(out).toEqual [
{id: 'a'},
{
type: "minifiedBundle"
messages: [{id: 'b'}, {id: 'c'}, {id: 'd'}]
},
{id: 'e'},
{id: 'f'},
{
type: "minifiedBundle"
messages: [{id: 'g'}, {id: 'h'}, {id: 'i'}, {id: 'j'}]
},
{id: 'k'},
{id: 'l'}
]