mirror of
https://github.com/Foundry376/Mailspring.git
synced 2024-11-12 04:25:31 +08:00
372 lines
12 KiB
Text
372 lines
12 KiB
Text
|
import Contact from '../../../src/flux/models/contact'
|
||
|
import Message from '../../../src/flux/models/message'
|
||
|
import Thread from '../../../src/flux/models/thread'
|
||
|
import Category from '../../../src/flux/models/category'
|
||
|
import CategoryStore from '../../../src/flux/stores/category-store'
|
||
|
import DatabaseStore from '../../../src/flux/stores/database-store'
|
||
|
import AccountStore from '../../../src/flux/stores/account-store'
|
||
|
import SoundRegistry from '../../../src/sound-registry'
|
||
|
import NativeNotifications from '../../../src/native-notifications'
|
||
|
import {Notifier} from '../lib/main'
|
||
|
|
||
|
describe("UnreadNotifications", function UnreadNotifications() {
|
||
|
beforeEach(() => {
|
||
|
this.notifier = new Notifier();
|
||
|
|
||
|
const inbox = new Category({id: "l1", name: "inbox", displayName: "Inbox"})
|
||
|
const archive = new Category({id: "l2", name: "archive", displayName: "Archive"})
|
||
|
|
||
|
spyOn(CategoryStore, "getStandardCategory").andReturn(inbox);
|
||
|
|
||
|
const account = AccountStore.accounts()[0];
|
||
|
|
||
|
this.threadA = new Thread({
|
||
|
categories: [inbox],
|
||
|
});
|
||
|
this.threadB = new Thread({
|
||
|
categories: [archive],
|
||
|
});
|
||
|
|
||
|
this.msg1 = new Message({
|
||
|
unread: true,
|
||
|
date: new Date(),
|
||
|
from: [new Contact({name: 'Ben', email: 'benthis.example.com'})],
|
||
|
subject: "Hello World",
|
||
|
threadId: "A",
|
||
|
});
|
||
|
this.msgNoSender = new Message({
|
||
|
unread: true,
|
||
|
date: new Date(),
|
||
|
from: [],
|
||
|
subject: "Hello World",
|
||
|
threadId: "A",
|
||
|
});
|
||
|
this.msg2 = new Message({
|
||
|
unread: true,
|
||
|
date: new Date(),
|
||
|
from: [new Contact({name: 'Mark', email: 'markthis.example.com'})],
|
||
|
subject: "Hello World 2",
|
||
|
threadId: "A",
|
||
|
});
|
||
|
this.msg3 = new Message({
|
||
|
unread: true,
|
||
|
date: new Date(),
|
||
|
from: [new Contact({name: 'Ben', email: 'benthis.example.com'})],
|
||
|
subject: "Hello World",
|
||
|
threadId: "A",
|
||
|
});
|
||
|
this.msg4 = new Message({
|
||
|
unread: true,
|
||
|
date: new Date(),
|
||
|
from: [new Contact({name: 'Ben', email: 'benthis.example.com'})],
|
||
|
subject: "Hello World",
|
||
|
threadId: "A",
|
||
|
});
|
||
|
this.msg5 = new Message({
|
||
|
unread: true,
|
||
|
date: new Date(),
|
||
|
from: [new Contact({name: 'Ben', email: 'benthis.example.com'})],
|
||
|
subject: "Hello World",
|
||
|
threadId: "A",
|
||
|
});
|
||
|
this.msgUnreadButArchived = new Message({
|
||
|
unread: true,
|
||
|
date: new Date(),
|
||
|
from: [new Contact({name: 'Mark', email: 'markthis.example.com'})],
|
||
|
subject: "Hello World 2",
|
||
|
threadId: "B",
|
||
|
});
|
||
|
this.msgRead = new Message({
|
||
|
unread: false,
|
||
|
date: new Date(),
|
||
|
from: [new Contact({name: 'Mark', email: 'markthis.example.com'})],
|
||
|
subject: "Hello World Read Already",
|
||
|
threadId: "A",
|
||
|
});
|
||
|
this.msgOld = new Message({
|
||
|
unread: true,
|
||
|
date: new Date(2000, 1, 1),
|
||
|
from: [new Contact({name: 'Mark', email: 'markthis.example.com'})],
|
||
|
subject: "Hello World Old",
|
||
|
threadId: "A",
|
||
|
});
|
||
|
this.msgFromMe = new Message({
|
||
|
unread: true,
|
||
|
date: new Date(),
|
||
|
from: [account.me()],
|
||
|
subject: "A Sent Mail!",
|
||
|
threadId: "A",
|
||
|
});
|
||
|
|
||
|
spyOn(DatabaseStore, 'find').andCallFake((klass, id) => {
|
||
|
if (id === 'A') {
|
||
|
return Promise.resolve(this.threadA);
|
||
|
}
|
||
|
if (id === 'B') {
|
||
|
return Promise.resolve(this.threadB);
|
||
|
}
|
||
|
return Promise.resolve(null);
|
||
|
});
|
||
|
|
||
|
spyOn(NativeNotifications, 'displayNotification').andCallFake(() => false)
|
||
|
spyOn(Promise, 'props').andCallFake((dict) => {
|
||
|
const dictOut = {};
|
||
|
for (const key of Object.keys(dict)) {
|
||
|
const val = dict[key];
|
||
|
if (val.value !== undefined) {
|
||
|
dictOut[key] = val.value();
|
||
|
} else {
|
||
|
dictOut[key] = val;
|
||
|
}
|
||
|
}
|
||
|
return Promise.resolve(dictOut);
|
||
|
});
|
||
|
});
|
||
|
|
||
|
afterEach(() => {
|
||
|
this.notifier.unlisten();
|
||
|
})
|
||
|
|
||
|
it("should create a Notification if there is one unread message", () => {
|
||
|
waitsForPromise(() => {
|
||
|
return this.notifier._onNewMailReceived({message: [this.msgRead, this.msg1]})
|
||
|
.then(() => {
|
||
|
advanceClock(2000)
|
||
|
expect(NativeNotifications.displayNotification).toHaveBeenCalled()
|
||
|
const options = NativeNotifications.displayNotification.mostRecentCall.args[0]
|
||
|
delete options.onActivate;
|
||
|
expect(options).toEqual({
|
||
|
title: 'Ben',
|
||
|
subtitle: 'Hello World',
|
||
|
body: undefined,
|
||
|
canReply: true,
|
||
|
tag: 'unread-update',
|
||
|
});
|
||
|
});
|
||
|
});
|
||
|
});
|
||
|
|
||
|
it("should create multiple Notifications if there is more than one but less than five unread messages", () => {
|
||
|
waitsForPromise(() => {
|
||
|
return this.notifier._onNewMailReceived({message: [this.msg1, this.msg2, this.msg3]})
|
||
|
.then(() => {
|
||
|
// Need to call advance clock twice because we call setTimeout twice
|
||
|
advanceClock(2000)
|
||
|
advanceClock(2000)
|
||
|
expect(NativeNotifications.displayNotification.callCount).toEqual(3)
|
||
|
});
|
||
|
});
|
||
|
});
|
||
|
|
||
|
it("should create a Notification if there are five or more unread messages", () => {
|
||
|
waitsForPromise(() => {
|
||
|
return this.notifier._onNewMailReceived({
|
||
|
message: [this.msg1, this.msg2, this.msg3, this.msg4, this.msg5]})
|
||
|
.then(() => {
|
||
|
advanceClock(2000)
|
||
|
expect(NativeNotifications.displayNotification).toHaveBeenCalled()
|
||
|
expect(NativeNotifications.displayNotification.mostRecentCall.args).toEqual([{
|
||
|
title: '5 Unread Messages',
|
||
|
tag: 'unread-update',
|
||
|
}])
|
||
|
});
|
||
|
});
|
||
|
});
|
||
|
|
||
|
it("should create a Notification correctly, even if new mail has no sender", () => {
|
||
|
waitsForPromise(() => {
|
||
|
return this.notifier._onNewMailReceived({message: [this.msgNoSender]})
|
||
|
.then(() => {
|
||
|
expect(NativeNotifications.displayNotification).toHaveBeenCalled()
|
||
|
|
||
|
const options = NativeNotifications.displayNotification.mostRecentCall.args[0]
|
||
|
delete options.onActivate;
|
||
|
expect(options).toEqual({
|
||
|
title: 'Unknown',
|
||
|
subtitle: 'Hello World',
|
||
|
body: undefined,
|
||
|
canReply: true,
|
||
|
tag: 'unread-update',
|
||
|
})
|
||
|
});
|
||
|
});
|
||
|
});
|
||
|
|
||
|
it("should not create a Notification if there are no new messages", () => {
|
||
|
waitsForPromise(() => {
|
||
|
return this.notifier._onNewMailReceived({message: []})
|
||
|
.then(() => {
|
||
|
expect(NativeNotifications.displayNotification).not.toHaveBeenCalled()
|
||
|
});
|
||
|
});
|
||
|
|
||
|
waitsForPromise(() => {
|
||
|
return this.notifier._onNewMailReceived({})
|
||
|
.then(() => {
|
||
|
expect(NativeNotifications.displayNotification).not.toHaveBeenCalled()
|
||
|
});
|
||
|
});
|
||
|
});
|
||
|
|
||
|
it("should not notify about unread messages that are outside the inbox", () => {
|
||
|
waitsForPromise(() => {
|
||
|
return this.notifier._onNewMailReceived({message: [this.msgUnreadButArchived, this.msg1]})
|
||
|
.then(() => {
|
||
|
expect(NativeNotifications.displayNotification).toHaveBeenCalled()
|
||
|
const options = NativeNotifications.displayNotification.mostRecentCall.args[0]
|
||
|
delete options.onActivate;
|
||
|
expect(options).toEqual({
|
||
|
title: 'Ben',
|
||
|
subtitle: 'Hello World',
|
||
|
body: undefined,
|
||
|
canReply: true,
|
||
|
tag: 'unread-update',
|
||
|
})
|
||
|
});
|
||
|
});
|
||
|
});
|
||
|
|
||
|
it("should not create a Notification if the new messages are not unread", () => {
|
||
|
waitsForPromise(() => {
|
||
|
return this.notifier._onNewMailReceived({message: [this.msgRead]})
|
||
|
.then(() => {
|
||
|
expect(NativeNotifications.displayNotification).not.toHaveBeenCalled()
|
||
|
});
|
||
|
});
|
||
|
});
|
||
|
|
||
|
it("should not create a Notification if the new messages are actually old ones", () => {
|
||
|
waitsForPromise(() => {
|
||
|
return this.notifier._onNewMailReceived({message: [this.msgOld]})
|
||
|
.then(() => {
|
||
|
expect(NativeNotifications.displayNotification).not.toHaveBeenCalled()
|
||
|
});
|
||
|
});
|
||
|
});
|
||
|
|
||
|
it("should not create a Notification if the new message is one I sent", () => {
|
||
|
waitsForPromise(() => {
|
||
|
return this.notifier._onNewMailReceived({message: [this.msgFromMe]})
|
||
|
.then(() => {
|
||
|
expect(NativeNotifications.displayNotification).not.toHaveBeenCalled()
|
||
|
});
|
||
|
});
|
||
|
});
|
||
|
|
||
|
it("should play a sound when it gets new mail", () => {
|
||
|
spyOn(NylasEnv.config, "get").andCallFake((config) => {
|
||
|
if (config === "core.notifications.enabled") return true
|
||
|
if (config === "core.notifications.sounds") return true
|
||
|
return undefined;
|
||
|
});
|
||
|
|
||
|
spyOn(SoundRegistry, "playSound");
|
||
|
waitsForPromise(() => {
|
||
|
return this.notifier._onNewMailReceived({message: [this.msg1]})
|
||
|
.then(() => {
|
||
|
expect(NylasEnv.config.get.calls[1].args[0]).toBe("core.notifications.sounds");
|
||
|
expect(SoundRegistry.playSound).toHaveBeenCalledWith("new-mail");
|
||
|
});
|
||
|
});
|
||
|
});
|
||
|
|
||
|
it("should not play a sound if the config is off", () => {
|
||
|
spyOn(NylasEnv.config, "get").andCallFake((config) => {
|
||
|
if (config === "core.notifications.enabled") return true;
|
||
|
if (config === "core.notifications.sounds") return false;
|
||
|
return undefined;
|
||
|
});
|
||
|
spyOn(SoundRegistry, "playSound")
|
||
|
waitsForPromise(() => {
|
||
|
return this.notifier._onNewMailReceived({message: [this.msg1]})
|
||
|
.then(() => {
|
||
|
expect(NylasEnv.config.get.calls[1].args[0]).toBe("core.notifications.sounds");
|
||
|
expect(SoundRegistry.playSound).not.toHaveBeenCalled()
|
||
|
});
|
||
|
});
|
||
|
});
|
||
|
|
||
|
it("should not play a sound if other notiications are still in flight", () => {
|
||
|
spyOn(NylasEnv.config, "get").andCallFake((config) => {
|
||
|
if (config === "core.notifications.enabled") return true;
|
||
|
if (config === "core.notifications.sounds") return true;
|
||
|
return undefined;
|
||
|
});
|
||
|
waitsForPromise(() => {
|
||
|
spyOn(SoundRegistry, "playSound")
|
||
|
return this.notifier._onNewMailReceived({message: [this.msg1, this.msg2]}).then(() => {
|
||
|
expect(SoundRegistry.playSound).toHaveBeenCalled();
|
||
|
SoundRegistry.playSound.reset();
|
||
|
return this.notifier._onNewMailReceived({message: [this.msg3]}).then(() => {
|
||
|
expect(SoundRegistry.playSound).not.toHaveBeenCalled();
|
||
|
});
|
||
|
});
|
||
|
});
|
||
|
});
|
||
|
|
||
|
describe("when the message has no matching thread", () => {
|
||
|
beforeEach(() => {
|
||
|
this.msgNoThread = new Message({
|
||
|
unread: true,
|
||
|
date: new Date(),
|
||
|
from: [new Contact({name: 'Ben', email: 'benthis.example.com'})],
|
||
|
subject: "Hello World",
|
||
|
threadId: "missing",
|
||
|
});
|
||
|
});
|
||
|
|
||
|
it("should not create a Notification, since it cannot be determined whether the message is in the Inbox", () => {
|
||
|
waitsForPromise(() => {
|
||
|
return this.notifier._onNewMailReceived({message: [this.msgNoThread]})
|
||
|
.then(() => {
|
||
|
advanceClock(2000)
|
||
|
expect(NativeNotifications.displayNotification).not.toHaveBeenCalled()
|
||
|
});
|
||
|
});
|
||
|
});
|
||
|
|
||
|
it("should call _onNewMessagesMissingThreads to try displaying a notification again in 10 seconds", () => {
|
||
|
waitsForPromise(() => {
|
||
|
spyOn(this.notifier, '_onNewMessagesMissingThreads')
|
||
|
return this.notifier._onNewMailReceived({message: [this.msgNoThread]})
|
||
|
.then(() => {
|
||
|
advanceClock(2000)
|
||
|
expect(this.notifier._onNewMessagesMissingThreads).toHaveBeenCalledWith([this.msgNoThread])
|
||
|
});
|
||
|
});
|
||
|
});
|
||
|
});
|
||
|
|
||
|
describe("_onNewMessagesMissingThreads", () => {
|
||
|
beforeEach(() => {
|
||
|
this.msgNoThread = new Message({
|
||
|
unread: true,
|
||
|
date: new Date(),
|
||
|
from: [new Contact({name: 'Ben', email: 'benthis.example.com'})],
|
||
|
subject: "Hello World",
|
||
|
threadId: "missing",
|
||
|
});
|
||
|
spyOn(this.notifier, '_onNewMailReceived')
|
||
|
this.notifier._onNewMessagesMissingThreads([this.msgNoThread])
|
||
|
advanceClock(2000)
|
||
|
});
|
||
|
|
||
|
it("should wait 10 seconds and then re-query for threads", () => {
|
||
|
expect(DatabaseStore.find).not.toHaveBeenCalled()
|
||
|
this.msgNoThread.threadId = "A"
|
||
|
advanceClock(10000)
|
||
|
expect(DatabaseStore.find).toHaveBeenCalled()
|
||
|
advanceClock()
|
||
|
expect(this.notifier._onNewMailReceived).toHaveBeenCalledWith({message: [this.msgNoThread], thread: [this.threadA]})
|
||
|
});
|
||
|
|
||
|
it("should do nothing if the threads still can't be found", () => {
|
||
|
expect(DatabaseStore.find).not.toHaveBeenCalled()
|
||
|
advanceClock(10000)
|
||
|
expect(DatabaseStore.find).toHaveBeenCalled()
|
||
|
advanceClock()
|
||
|
expect(this.notifier._onNewMailReceived).not.toHaveBeenCalled()
|
||
|
});
|
||
|
});
|
||
|
});
|