Mailspring/app/internal_packages/unread-notifications/specs/main-spec.es6

396 lines
12 KiB
Text
Raw Normal View History

import {
Contact,
Message,
Thread,
Folder,
CategoryStore,
DatabaseStore,
AccountStore,
SoundRegistry,
NativeNotifications,
} from 'nylas-exports';
import {Notifier} from '../lib/main'
describe("UnreadNotifications", function UnreadNotifications() {
beforeEach(() => {
this.notifier = new Notifier();
const inbox = new Folder({id: "l1", role: "inbox", path: "Inbox"})
const archive = new Folder({id: "l2", role: "archive", path: "Archive"})
spyOn(CategoryStore, "getCategoryByRole").andReturn(inbox);
const account = AccountStore.accounts()[0];
this.threadA = new Thread({
id: 'A',
folders: [inbox],
});
this.threadB = new Thread({
id: 'B',
folders: [archive],
});
this.msg1 = new Message({
unread: true,
date: new Date(),
from: [new Contact({name: 'Ben', email: 'benthis.example.com'})],
subject: "Hello World",
threadId: "A",
version: 1,
});
this.msgNoSender = new Message({
unread: true,
date: new Date(),
from: [],
subject: "Hello World",
threadId: "A",
version: 1,
});
this.msg2 = new Message({
unread: true,
date: new Date(),
from: [new Contact({name: 'Mark', email: 'markthis.example.com'})],
subject: "Hello World 2",
threadId: "A",
version: 1,
});
this.msg3 = new Message({
unread: true,
date: new Date(),
from: [new Contact({name: 'Ben', email: 'benthis.example.com'})],
subject: "Hello World 3",
threadId: "A",
version: 1,
});
this.msg4 = new Message({
unread: true,
date: new Date(),
from: [new Contact({name: 'Ben', email: 'benthis.example.com'})],
subject: "Hello World 4",
threadId: "A",
version: 1,
});
this.msg5 = new Message({
unread: true,
date: new Date(),
from: [new Contact({name: 'Ben', email: 'benthis.example.com'})],
subject: "Hello World 5",
threadId: "A",
version: 1,
});
this.msgUnreadButArchived = new Message({
unread: true,
date: new Date(),
from: [new Contact({name: 'Mark', email: 'markthis.example.com'})],
subject: "Hello World 2",
threadId: "B",
version: 1,
});
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",
version: 1,
});
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",
version: 1,
});
this.msgFromMe = new Message({
unread: true,
date: new Date(),
from: [account.me()],
subject: "A Sent Mail!",
threadId: "A",
version: 1,
});
this.msgHigherVersion = new Message({
unread: true,
date: new Date(),
from: [new Contact({name: 'Ben', email: 'benthis.example.com'})],
subject: "Hello World",
threadId: "A",
version: 2,
})
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(DatabaseStore, 'findAll').andCallFake(() => {
return Promise.resolve([this.threadA, this.threadB]);
});
this.notification = jasmine.createSpyObj('notification', ['close']);
spyOn(NativeNotifications, 'displayNotification').andReturn(this.notification);
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(async () => {
await this.notifier._onDatabaseChanged({
objectClass: Message.name,
objects: [this.msgRead, this.msg1],
})
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(async () => {
await this.notifier._onDatabaseChanged({
objectClass: Message.name,
objects: [this.msg1, this.msg2, this.msg3],
})
// Need to call advance clock twice because we call setTimeout twice
advanceClock(2000)
advanceClock(2000)
expect(NativeNotifications.displayNotification.callCount).toEqual(3)
});
});
it("should create Notifications in the order of messages received", () => {
waitsForPromise(async () => {
await this.notifier._onDatabaseChanged({
objectClass: Message.name,
objects: [this.msg1, this.msg2],
})
advanceClock(2000);
await this.notifier._onDatabaseChanged({
objectClass: Message.name,
objects: [this.msg3, this.msg4],
})
advanceClock(2000);
advanceClock(2000);
expect(NativeNotifications.displayNotification.callCount).toEqual(4);
const subjects = NativeNotifications.displayNotification.calls.map((call) => {
return call.args[0].subtitle;
});
const expected = [this.msg1, this.msg2, this.msg3, this.msg4]
.map((msg) => msg.subject);
expect(subjects).toEqual(expected);
});
});
it("should create a Notification if there are five or more unread messages", () => {
waitsForPromise(async () => {
await this.notifier._onDatabaseChanged({
objectClass: Message.name,
objects: [this.msg1, this.msg2, this.msg3, this.msg4, this.msg5],
})
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(async () => {
await this.notifier._onDatabaseChanged({
objectClass: Message.name,
objects: [this.msgNoSender],
});
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(async () => {
await this.notifier._onDatabaseChanged({
objectClass: Message.name,
objects: [],
})
expect(NativeNotifications.displayNotification).not.toHaveBeenCalled()
await this.notifier._onDatabaseChanged({});
expect(NativeNotifications.displayNotification).not.toHaveBeenCalled()
});
});
it("should not notify about unread messages that are outside the inbox", () => {
waitsForPromise(async () => {
await this.notifier._onDatabaseChanged({
objectClass: Message.name,
objects: [this.msgUnreadButArchived, this.msg1],
})
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 read", () => {
waitsForPromise(async () => {
await this.notifier._onDatabaseChanged({
objectClass: Message.name,
objects: [this.msgRead],
})
expect(NativeNotifications.displayNotification).not.toHaveBeenCalled()
});
});
it("should not create a Notification if the message model is being updated", () => {
waitsForPromise(async () => {
await this.notifier._onDatabaseChanged({
objectClass: Message.name,
objects: [this.msgHigherVersion],
})
expect(NativeNotifications.displayNotification).not.toHaveBeenCalled()
});
});
it("should not create a Notification if the new messages are actually old ones", () => {
waitsForPromise(async () => {
await this.notifier._onDatabaseChanged({
objectClass: Message.name,
objects: [this.msgOld],
})
expect(NativeNotifications.displayNotification).not.toHaveBeenCalled()
});
});
it("should not create a Notification if the new message is one I sent", () => {
waitsForPromise(async () => {
await this.notifier._onDatabaseChanged({
objectClass: Message.name,
objects: [this.msgFromMe],
})
expect(NativeNotifications.displayNotification).not.toHaveBeenCalled()
});
});
it("clears notifications when a thread is read", () => {
waitsForPromise(async () => {
await this.notifier._onDatabaseChanged({
objectClass: Message.name,
objects: [this.msg1],
})
expect(NativeNotifications.displayNotification).toHaveBeenCalled();
expect(this.notification.close).not.toHaveBeenCalled();
const read = this.threadA.clone();
read.unread = false;
await this.notifier._onDatabaseChanged({
objectClass: Thread.name,
objects: [read],
})
expect(this.notification.close).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(async () => {
await this.notifier._onDatabaseChanged({
objectClass: Message.name,
objects: [this.msg1],
})
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(async () => {
await this.notifier._onDatabaseChanged({
objectClass: Message.name,
objects: [this.msg1],
})
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(async () => {
spyOn(SoundRegistry, "playSound")
await this.notifier._onDatabaseChanged({
objectClass: Message.name,
objects: [this.msg1, this.msg2],
})
expect(SoundRegistry.playSound).toHaveBeenCalled();
SoundRegistry.playSound.reset();
await this.notifier._onDatabaseChanged({
objectClass: Message.name,
objects: [this.msg3],
})
expect(SoundRegistry.playSound).not.toHaveBeenCalled();
});
});
});