VulpesOS/gaia/apps/sms/services/test/unit/drafts_test.js
OverSu b38dea0597 Publish Vulpes Preview 15 sources for desktop, Android and Tundra
Share Gaia assets and adapters, keep private profiles and development documents local, and update both public READMEs.
2026-09-30 23:13:03 +02:00

659 lines
17 KiB
JavaScript
Executable file

/*global asyncStorage,
Drafts,
Draft,
InterInstanceEventDispatcher,
MocksHelper
*/
'use strict';
require('/shared/js/event_dispatcher.js');
require('/views/shared/js/utils.js');
require('/services/js/drafts.js');
require('/shared/test/unit/mocks/mock_async_storage.js');
require('/views/shared/test/unit/mock_inter_instance_event_dispatcher.js');
var MocksHelperForDraftsTest = new MocksHelper([
'asyncStorage',
'InterInstanceEventDispatcher'
]).init();
suite('Drafts', function() {
var mocksHelper = MocksHelperForDraftsTest;
var threadDraft1, threadDraft2, threadDraft3, threadDraft4, threadDraft5;
var draft1, draft2;
suiteSetup(function() {
mocksHelper.suiteSetup();
threadDraft1 = new Draft({
recipients: ['555', '666'],
content: ['This is a draft message'],
timestamp: 1,
threadId: 42,
subject: 'This is a subject',
type: 'sms',
id: 1
});
threadDraft2 = new Draft({
recipients: ['555'],
content: ['This is a draft message'],
timestamp: 2,
threadId: 44,
subject: 'This is a subject',
type: 'sms',
id: 2
});
threadDraft3 = new Draft({
recipients: ['555', '222'],
content: ['This is a draft message'],
timestamp: 3,
threadId: 1,
subject: 'This is a subject',
type: 'sms',
id: 3
});
threadDraft4 = new Draft({
recipients: ['555', '333'],
content: ['This is a draft message'],
timestamp: 4,
threadId: 2,
subject: 'This is a subject',
type: 'sms',
id: 4
});
draft1 = new Draft({
recipients: ['555', '444'],
content: ['This is a draft message'],
timestamp: 5,
threadId: null,
subject: 'This is a subject',
type: 'sms',
id: 5
});
threadDraft5 = new Draft({
recipients: ['123456'],
content: [
'This is a draft MMS...',
{
blob: {
type: 'audio/ogg',
size: 12345
},
name: 'audio.oga'
},
'...with a recipient and a thread'
],
timestamp: Date.now() - (3600000 * 2),
threadId: 8,
type: 'mms',
id: 6
});
draft2 = new Draft({
recipients: ['555', '444'],
content: ['This is a draft message'],
timestamp: 5,
threadId: null,
subject: 'This is a different subject',
type: 'sms',
id: 7
});
});
suiteTeardown(function() {
mocksHelper.suiteTeardown();
});
suite('init() >', function() {
var savedHandler;
var deletedHandler;
setup(function() {
this.sinon.spy(InterInstanceEventDispatcher, 'on');
this.sinon.stub(Drafts, 'request').returns(Promise.resolve());
this.sinon.stub(Drafts, 'emit');
Drafts.init();
savedHandler = InterInstanceEventDispatcher.on.firstCall.args[1];
deletedHandler = InterInstanceEventDispatcher.on.secondCall.args[1];
});
test('draft-saved will be dispatched as saved', function(done) {
sinon.assert.calledWith(InterInstanceEventDispatcher.on, 'draft-saved');
savedHandler(threadDraft1).then(() => {
sinon.assert.calledWith(Drafts.request, true);
sinon.assert.calledWith(Drafts.emit, 'saved', threadDraft1);
}).then(done, done);
});
test('draft-deleted will be dispatched as deleted', function(done) {
sinon.assert.calledWith(InterInstanceEventDispatcher.on, 'draft-deleted');
deletedHandler(threadDraft2).then(() => {
sinon.assert.calledWith(Drafts.request, true);
sinon.assert.calledWith(Drafts.emit, 'deleted', threadDraft2);
}).then(done, done);
});
});
suite('add() >', function() {
var onSavedStub;
setup(function() {
onSavedStub = sinon.stub();
Drafts.on('saved', onSavedStub);
this.sinon.spy(InterInstanceEventDispatcher, 'emit');
});
teardown(function() {
Drafts.clear().offAll('saved');
});
test('correctly adds thread bound drafts', function() {
Drafts.add(threadDraft1);
assert.deepEqual(Drafts.byThreadId(threadDraft1.threadId), threadDraft1);
assert.isNull(Drafts.byThreadId(threadDraft2.threadId));
sinon.assert.calledOnce(onSavedStub);
sinon.assert.calledWith(onSavedStub, threadDraft1);
sinon.assert.calledWith(
InterInstanceEventDispatcher.emit,
'draft-saved',
threadDraft1
);
Drafts.add(threadDraft2);
assert.deepEqual(Drafts.byThreadId(threadDraft1.threadId), threadDraft1);
assert.deepEqual(Drafts.byThreadId(threadDraft2.threadId), threadDraft2);
sinon.assert.calledTwice(onSavedStub);
sinon.assert.calledWith(onSavedStub, threadDraft2);
sinon.assert.calledWith(
InterInstanceEventDispatcher.emit,
'draft-saved',
threadDraft2
);
});
test('correctly adds thread less drafts', function() {
Drafts.add(draft1);
assert.deepEqual(Drafts.byDraftId(draft1.id), draft1);
assert.isNull(Drafts.byDraftId(draft2.id));
sinon.assert.calledOnce(onSavedStub);
sinon.assert.calledWith(onSavedStub, draft1);
sinon.assert.calledWith(
InterInstanceEventDispatcher.emit,
'draft-saved',
draft1
);
Drafts.add(draft2);
assert.deepEqual(Drafts.byDraftId(draft1.id), draft1);
assert.deepEqual(Drafts.byDraftId(draft2.id), draft2);
sinon.assert.calledTwice(onSavedStub);
sinon.assert.calledWith(onSavedStub, draft2);
sinon.assert.calledWith(
InterInstanceEventDispatcher.emit,
'draft-saved',
draft2
);
});
test('add draft of same threadId replaces previous', function() {
assert.equal(
Drafts.add(threadDraft2).byThreadId(threadDraft2.threadId).id,
threadDraft2.id
);
sinon.assert.calledOnce(onSavedStub);
sinon.assert.calledWith(onSavedStub, threadDraft2);
sinon.assert.calledWith(
InterInstanceEventDispatcher.emit,
'draft-saved',
threadDraft2
);
var updatedDraft = new Draft({
recipients: ['555'],
content: ['This is a new draft for thread 44'],
subject: 'This is a subject',
timestamp: 2,
threadId: 44,
type: 'sms'
});
Drafts.add(updatedDraft);
assert.notEqual(Drafts.byThreadId(44).id, threadDraft2.id);
sinon.assert.calledTwice(onSavedStub);
sinon.assert.calledWith(onSavedStub, updatedDraft);
sinon.assert.calledWith(
InterInstanceEventDispatcher.emit,
'draft-saved',
updatedDraft
);
});
test('add threadless draft of same draft.id replaces previous', function() {
assert.equal(
Drafts.add(draft1).byDraftId(draft1.id).content,
'This is a draft message'
);
sinon.assert.calledOnce(onSavedStub);
sinon.assert.calledWith(onSavedStub, draft1);
sinon.assert.calledWith(
InterInstanceEventDispatcher.emit,
'draft-saved',
draft1
);
var updatedDraft = new Draft({
recipients: ['555', '444'],
content: ['This is a new draft message'],
timestamp: 5,
threadId: null,
subject: 'This is a subject',
type: 'sms',
id: 5
});
Drafts.add(updatedDraft);
assert.equal(
Drafts.byDraftId(draft1.id).content,
'This is a new draft message'
);
sinon.assert.calledTwice(onSavedStub);
sinon.assert.calledWith(onSavedStub, updatedDraft);
sinon.assert.calledWith(
InterInstanceEventDispatcher.emit,
'draft-saved',
updatedDraft
);
});
});
suite('delete() >', function() {
var onDeletedStub;
setup(function() {
onDeletedStub = sinon.stub();
Drafts.on('deleted', onDeletedStub);
[threadDraft1, threadDraft2, threadDraft5, draft2].forEach(
Drafts.add, Drafts
);
this.sinon.spy(InterInstanceEventDispatcher, 'emit');
});
teardown(function() {
Drafts.clear().offAll('deleted');
});
test('Delete draft with reference', function() {
assert.isNotNull(Drafts.byThreadId(threadDraft1.threadId));
assert.isNull(
Drafts.delete(threadDraft1).byThreadId(threadDraft1.threadId)
);
sinon.assert.calledOnce(onDeletedStub);
sinon.assert.calledWith(onDeletedStub, threadDraft1);
sinon.assert.calledWith(
InterInstanceEventDispatcher.emit,
'draft-deleted',
threadDraft1
);
});
test('Deleting new message drafts', function() {
assert.isNotNull(Drafts.byThreadId(threadDraft5.threadId));
assert.isNotNull(Drafts.byDraftId(draft2.id));
// First draft removes only the draft from the drafts
assert.isNull(
Drafts.delete(threadDraft5).byThreadId(threadDraft5.threadId)
);
assert.isNotNull(Drafts.byDraftId(draft2.id));
sinon.assert.calledOnce(onDeletedStub);
sinon.assert.calledWith(onDeletedStub, threadDraft5);
sinon.assert.calledWith(
InterInstanceEventDispatcher.emit,
'draft-deleted',
threadDraft5
);
// The last draft in the thread removes the thread from the index
assert.isNull(Drafts.delete(draft2).byDraftId(draft2.id));
sinon.assert.calledTwice(onDeletedStub);
sinon.assert.calledWith(onDeletedStub, draft2);
sinon.assert.calledWith(
InterInstanceEventDispatcher.emit,
'draft-deleted',
draft2
);
});
test('delete by non-draft object', function() {
var draft = new Draft({
recipients: ['999999'],
content: ['foo'],
timestamp: 2,
threadId: 999,
subject: 'This is a subject',
type: 'sms'
});
Drafts.add(draft);
assert.deepEqual(Drafts.byThreadId(999), draft);
var draftToDelete = new Draft({
threadId: 999,
id: draft.id
});
Drafts.delete(draftToDelete);
assert.isNull(Drafts.byThreadId(999));
sinon.assert.calledOnce(onDeletedStub);
sinon.assert.calledWith(onDeletedStub, draftToDelete);
sinon.assert.calledWith(
InterInstanceEventDispatcher.emit,
'draft-deleted',
draftToDelete
);
});
test('does not fire "deleted" event if nothing was deleted', function() {
// Try to delete draft that doesn't exist.
Drafts.delete(draft1);
sinon.assert.notCalled(onDeletedStub);
sinon.assert.notCalled(InterInstanceEventDispatcher.emit);
});
});
suite('Select drafts', function() {
suiteSetup(function() {
[threadDraft1, threadDraft2, threadDraft3, threadDraft4, draft1].forEach(
Drafts.add, Drafts
);
});
suiteTeardown(function() {
Drafts.clear();
});
suite('byThreadId', function() {
test('get drafts for id 1', function() {
assert.deepEqual(
Drafts.byThreadId(threadDraft3.threadId), threadDraft3
);
});
test('throws if threadId is not a number', function() {
assert.throws(() => Drafts.byThreadId(null));
});
test('returns null if there is no draft for the thread id', function() {
assert.isNull(Drafts.byThreadId(10));
});
});
});
suite('get(id) > ', function() {
var id = 101;
suiteSetup(function() {
Drafts.add(new Draft({
id: id,
recipients: [],
content: ['A new message draft with no recipients'],
type: 'sms'
}));
});
suiteTeardown(function() {
Drafts.clear();
});
test('get draft for id 101', function() {
var draft = Drafts.byDraftId(id);
assert.equal(draft.id, id);
assert.equal(draft.threadId, null);
});
test('get no draft for non-existant id', function() {
assert.isNull(Drafts.byDraftId(9999999));
});
});
suite('getAll()>', function() {
setup(function() {
[
threadDraft1,
threadDraft2,
threadDraft3,
threadDraft4,
threadDraft5,
draft1,
draft2
].forEach(Drafts.add, Drafts);
});
teardown(function() {
Drafts.clear();
});
test('getAll returns all drafts', function() {
var orderedDrafts = [
threadDraft1,
threadDraft2,
threadDraft3,
threadDraft4,
threadDraft5,
draft1,
draft2
];
for (var draft of Drafts.getAll()) {
assert.deepEqual(draft, orderedDrafts.shift());
}
// All drafts were examined
assert.equal(orderedDrafts.length, 0);
});
});
suite('getAllThreadless()>', function() {
setup(function() {
[
threadDraft1,
draft1,
draft2
].forEach(Drafts.add, Drafts);
});
teardown(function() {
Drafts.clear();
});
test('getAllThreadless returns only thread less drafts', function() {
assert.deepEqual(Drafts.getAllThreadless(), [draft1, draft2]);
});
});
suite('clear() >', function() {
suiteSetup(function() {
[threadDraft1, threadDraft2, threadDraft3, threadDraft4].forEach(
Drafts.add, Drafts
);
});
test('clear the entire draft index', function() {
Drafts.clear();
assert.isNull(Drafts.byThreadId(42));
assert.isNull(Drafts.byThreadId(1));
assert.isNull(Drafts.byThreadId(2));
});
});
suite('Draft', function() {
var draft;
test('Draft', function() {
draft = new Draft();
assert.ok(Draft);
});
test('Draft from empty object', function() {
draft = new Draft([]);
assert.deepEqual(draft.recipients, []);
assert.deepEqual(draft.content, []);
assert.equal(draft.threadId, null);
assert.isFalse(draft.isEdited);
});
test('Draft from Draft object', function() {
draft = new Draft(threadDraft1);
assert.deepEqual(draft.recipients, ['555', '666']);
assert.deepEqual(draft.content, ['This is a draft message']);
assert.equal(draft.timestamp, 1);
assert.equal(draft.threadId, 42);
assert.equal(draft.type, 'sms');
assert.isFalse(draft.isEdited);
});
test('Draft with explicit valid id', function() {
draft = new Draft({
id: 101,
recipients: [],
content: ['An explicit id'],
type: 'sms'
});
assert.equal(draft.id, 101);
});
test('Draft with explicit invalid id', function() {
try {
draft = new Draft({
id: '101',
recipients: [],
content: ['An explicit id'],
timestamp: Date.now(),
type: 'sms'
});
assert.ok(false);
} catch (e) {
assert.ok(true);
}
});
});
suite('Storage and Retrieval', function() {
setup(function() {
Drafts.clear();
this.sinon.useFakeTimers();
});
test('Load drafts, has stored data', function(done) {
this.sinon.stub(asyncStorage, 'getItem').yields([
[42, [threadDraft1]],
[44, [threadDraft2]],
[null, [draft1, draft2]]
]);
Drafts.request().then(function() {
assert.deepEqual(Drafts.byThreadId(42), threadDraft1);
assert.deepEqual(Drafts.byThreadId(44), threadDraft2);
var threadLessDrafts = Array.from(Drafts.getAll()).filter(
(draft) => !draft.threadId
);
assert.equal(threadLessDrafts.length, 2);
assert.isNull(Drafts.byThreadId(5));
}).then(done, done);
});
test('Load, clear, restore drafts', function(done) {
this.sinon.stub(asyncStorage, 'getItem').yields([
[42, [threadDraft1]],
[44, [threadDraft2]],
[null, [draft1, draft2]]
]);
// Load
Drafts.request().then(function() {
assert.equal(Drafts.size, 3);
assert.isTrue(asyncStorage.getItem.calledOnce);
// Clear (This will set isCached = false)
Drafts.clear();
assert.equal(Drafts.size, 0);
// Restore
return Drafts.request().then(function() {
assert.equal(Drafts.size, 3);
assert.isTrue(asyncStorage.getItem.calledTwice);
});
}).then(done, done);
});
test('Load drafts, has no stored data', function(done) {
this.sinon.stub(asyncStorage, 'getItem').yields([]);
Drafts.request().then(function() {
assert.equal(Drafts.size, 0);
}).then(done, done);
});
suite('drafts index cache >', function() {
setup(function() {
this.sinon.stub(asyncStorage, 'getItem').
withArgs('draft index').
yields(null);
});
test('is properly populated', function(done) {
Drafts.request().then(() => {
sinon.assert.calledOnce(asyncStorage.getItem);
return Drafts.request();
}).then(() => {
sinon.assert.calledOnce(asyncStorage.getItem);
}).then(done, done);
});
test('is properly refreshed with force parameter', function(done) {
Drafts.request().then(() => {
sinon.assert.calledOnce(asyncStorage.getItem);
// "Force" parameter should re-request data from asyncStorage even if
// it's already cached
return Drafts.request(true);
}).then(() => {
sinon.assert.calledTwice(asyncStorage.getItem);
}).then(done, done);
});
});
});
});