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.
This commit is contained in:
OverSu 2026-09-30 23:13:03 +02:00
commit b38dea0597
25901 changed files with 9982925 additions and 369 deletions

View file

@ -0,0 +1,17 @@
{
"extends": "../../../../.jshintrc",
"predef": [
"assert",
"suite",
"test",
"setup",
"teardown",
"suiteSetup",
"suiteTeardown",
"require",
"requireApp",
"sinon",
"mocha",
"loadBodyHTML"
]
}

View file

@ -0,0 +1,91 @@
'use strict';
/*global requireApp, suite, setup, testConfig, test, assert, suiteSetup,
suiteTeardown */
requireApp('email/js/alameda.js');
requireApp('email/test/config.js');
suite('compose', function() {
var subject, Compose;
var mockAttachments = [{
name: 'attachment1.jpg',
blob: { size: 10000, type: 'type1' }
}, {
name: 'attachment2.jpg',
blob: { size: 20000, type: 'type2' }
}];
function MockComposer() {
this.attachments = [];
this.addAttachment = function(attachmentDef) {
this.attachments.push({
name: attachmentDef.name,
blob: {
size: attachmentDef.blob.size,
type: attachmentDef.blob.type
}
});
};
}
function testUpdateAttachmentsAriaLabel(expectedTotalSizeInKb) {
subject.updateAttachmentsAriaLabel();
assert.equal(subject.attachmentsContainer.getAttribute('data-l10n-id'),
'compose-attachments-container');
var args = subject.attachmentsContainer.getAttribute('data-l10n-args');
assert.deepEqual(JSON.parse(args), {kilobytes: expectedTotalSizeInKb || 0});
}
suiteSetup(function(done) {
testConfig({
suiteTeardown: suiteTeardown,
done: done
}, ['element!cards/compose'], function(c) {
Compose = c;
});
});
setup(function() {
subject = new Compose();
subject.onArgs({ composer: new MockComposer() });
});
suite('zero attachments', function() {
test('calculateTotalAttachmentsSize', function() {
assert.equal(subject.calculateTotalAttachmentsSize(), 0);
});
test('updateAttachmentsAriaLabel', function() {
testUpdateAttachmentsAriaLabel();
});
});
suite('one attachment', function() {
setup(function() {
subject.composer.addAttachment(mockAttachments[0]);
});
test('calculateTotalAttachmentsSize', function() {
assert.equal(subject.calculateTotalAttachmentsSize(), 10000);
});
test('updateAttachmentsAriaLabel', function() {
testUpdateAttachmentsAriaLabel();
});
});
suite('more than one attachment', function() {
setup(function() {
subject.composer.addAttachment(mockAttachments[0]);
subject.composer.addAttachment(mockAttachments[1]);
});
test('calculateTotalAttachmentsSize', function() {
assert.equal(subject.calculateTotalAttachmentsSize(), 30000);
});
test('updateAttachmentsAriaLabel', function() {
testUpdateAttachmentsAriaLabel(Math.ceil(30000 / 1024));
});
});
});

View file

@ -0,0 +1,293 @@
/*jshint browser: true */
/*global requireApp, suite, setup, testConfig, test, assert,
suiteSetup, suiteTeardown */
'use strict';
requireApp('email/js/alameda.js');
requireApp('email/test/config.js');
suite('evt', function() {
var evt;
suiteSetup(function(done) {
testConfig(
{
suiteTeardown: suiteTeardown,
done: done
},
['evt'],
function(e) {
evt = e;
}
);
});
suite('#on', function() {
test('basic use', function() {
var count = 0;
evt.on('testOn', function() {
count += 1;
});
evt.emit('testOn');
evt.emit('testOn');
//evt calls listeners synchronously, so should be complete now.
assert.equal(count, 2);
});
test('multiple args', function() {
var count = 0;
evt.on('testOn', function(first, second) {
count += 1;
if (count === 1) {
assert.equal(first, 'one');
assert.equal(second, 'two');
} else if (count === 2) {
assert.equal(first, 'three');
assert.equal(second, 'four');
}
});
evt.emit('testOn', 'one', 'two');
evt.emit('testOn', 'three', 'four');
});
test('only catch second emit', function() {
var count = 0;
evt.emit('testOn2');
evt.on('testOn2', function() {
count += 1;
});
evt.emit('testOn2');
assert.equal(count, 1);
});
});
suite('#once', function() {
test('basic use', function() {
var count = 0;
evt.once('testOnce', function() {
count += 1;
});
evt.emit('testOnce');
evt.emit('testOnce');
assert.equal(count, 1);
});
});
suite('#removeListener', function() {
test('basic use', function() {
var count = 0;
function onEvent() {
count += 1;
evt.removeListener('testRemoveListener', onEvent);
}
evt.on('testRemoveListener', onEvent);
assert.equal(evt._events.hasOwnProperty('testRemoveListener'), true);
evt.emit('testRemoveListener');
evt.emit('testRemoveListener');
assert.equal(count, 1);
assert.equal(evt._events.hasOwnProperty('testRemoveListener'), false);
});
});
suite('#emitWhenListener', function() {
test('basic use', function() {
var count = 0;
evt.emitWhenListener('testEmitWhenListener');
evt.on('testEmitWhenListener', function() {
count += 1;
});
evt.emit('testEmitWhenListener');
assert.equal(count, 2);
});
});
suite('#latest', function() {
var data;
// Executed for each test() below
setup(function() {
data = {
setToken: function(value) {
this.token = value;
this.emitWhenListener('token', this.token);
},
token: null
};
evt.mix(data);
});
test('delayed value', function() {
var count = 0;
data.latest('token', function(token) {
count += 1;
if (count === 1) {
assert.equal(token, 'one');
} else if (count === 2) {
assert.equal(token, 'two');
}
});
data.setToken('one');
data.setToken('two');
assert.equal(count, 2);
});
test('immediate value', function() {
var count = 0;
data.setToken('one');
data.latest('token', function(token) {
count += 1;
if (count === 1) {
assert.equal(token, 'one');
} else if (count === 2) {
assert.equal(token, 'two');
}
});
assert.equal(count, 1);
data.setToken('two');
assert.equal(count, 2);
});
test('immediate value, after first listener', function() {
var count = 0;
data.setToken('one');
data.latest('token', function(token) {
count += 1;
assert.equal(token, 'one');
// Second latest, which should trigger the callback
// even though there is no pending emit.
data.latest('token', function(token) {
count += 1;
assert.equal(token, 'one');
assert.equal(count, 2);
});
});
assert.equal(count, 2);
});
});
suite('#latestOnce', function() {
var data;
// Executed for each test() below
setup(function() {
data = {
setToken: function(value) {
this.token = value;
this.emitWhenListener('token', this.token);
},
token: null
};
evt.mix(data);
});
test('double latestOnce listeners', function() {
var count = 0;
function onLatest(token) {
count += 1;
assert.equal(token, 'one');
}
data.latestOnce('token', onLatest);
data.latestOnce('token', onLatest);
data.setToken('one');
assert.equal(count, 2);
});
test('delayed value', function() {
var count = 0;
data.latestOnce('token', function(token) {
count += 1;
assert.equal(token, 'one');
});
data.setToken('one');
data.setToken('two');
assert.equal(count, 1);
});
test('immediate value', function() {
var count = 0;
data.setToken('one');
data.latestOnce('token', function(token) {
count += 1;
assert.equal(token, 'one');
});
assert.equal(count, 1);
data.setToken('two');
assert.equal(count, 1);
});
test('immediate value, after first listener', function() {
var count = 0;
data.setToken('one');
data.latestOnce('token', function(token) {
count += 1;
assert.equal(token, 'one');
// Second latestOnce, which should trigger the callback
// even though there is no pending emit.
data.latestOnce('token', function(token) {
count += 1;
assert.equal(token, 'one');
});
});
assert.equal(count, 2);
});
});
});

View file

@ -0,0 +1,112 @@
'use strict';
/* global testConfig */
requireApp('email/js/alameda.js');
requireApp('email/test/config.js');
/**
* Fake MailHeader instances for our cursor's messagesSlice.
*/
var MESSAGE_ONE = Object.freeze({ id: 'one' }),
MESSAGE_TWO = Object.freeze({ id: 'two' }),
MESSAGE_THREE = Object.freeze({ id: 'three' });
suite('HeaderCursor', function() {
var evt, headerCursor, CurrentMessage;
suiteSetup(function(done) {
testConfig({ done: done }, [
'evt',
'header_cursor',
'model_create',
], function(_evt, HeaderCursor, mc) {
evt = _evt;
headerCursor = new HeaderCursor(mc.defaultModel);
CurrentMessage = HeaderCursor.CurrentMessage;
});
});
setup(function() {
headerCursor.messagesSlice = {
die: function() {},
items: [
MESSAGE_ONE,
MESSAGE_TWO,
MESSAGE_THREE
]
};
headerCursor.currentMessage = new CurrentMessage(
MESSAGE_TWO, {
hasPrevious: true,
hasNext: true
}
);
});
suite('#advance', function() {
test('next should go to next', function(done) {
headerCursor.once('currentMessage', function(currentMessage) {
assert.deepEqual(
currentMessage,
new CurrentMessage(MESSAGE_THREE, {
hasPrevious: true,
hasNext: false
})
);
done();
});
headerCursor.advance('next');
});
test('previous should go to previous', function(done) {
headerCursor.once('currentMessage', function(currentMessage) {
assert.deepEqual(
currentMessage,
new CurrentMessage(MESSAGE_ONE, {
hasPrevious: false,
hasNext: true
})
);
done();
});
headerCursor.advance('previous');
});
test('should not die if advance out of bounds', function() {
headerCursor.currentMessage = new CurrentMessage(
MESSAGE_THREE, {
hasPrevious: true,
hasNext: false
}
);
// If this doesn't error, life is good!
headerCursor.advance('next');
});
});
suite('#indexOfMessageById', function() {
test('should be correct if contains message with id', function() {
assert.ok(headerCursor.indexOfMessageById('two'), 2);
});
test('should be -1 if not contains message with id', function() {
assert.ok(headerCursor.indexOfMessageById('purple'), -1);
});
});
suite('#die', function() {
test('should die messagesSlice', function() {
var die = sinon.stub(headerCursor.messagesSlice, 'die');
headerCursor.die();
sinon.assert.called(die);
assert.strictEqual(headerCursor.messagesSlice, null);
});
});
});

View file

@ -0,0 +1,126 @@
'use strict';
/*global requireApp, suite, suiteSetup, testConfig, test,
assert, suiteTeardown */
requireApp('email/js/alameda.js');
requireApp('email/test/config.js');
suite('email/query_uri', function() {
var queryURI;
suiteSetup(function(done) {
testConfig(
{
suiteTeardown: suiteTeardown,
done: done
},
['query_uri'],
function(quri) {
queryURI = quri;
}
);
});
test('#to', function() {
assert.deepEqual(queryURI('mailto:Email.address1@mailto.com'),
{ to: ['Email.address1@mailto.com'] },
'to single address test fail');
assert.deepEqual(queryURI(
'mailto:Email.address1@mailto.com;Email.address2@mailto.com'),
{ to: ['Email.address1@mailto.com', 'Email.address2@mailto.com'] },
'to multi-addresses test fail (separator ";")');
assert.deepEqual(queryURI(
'mailto:Email.address1@mailto.com,Email.address2@mailto.com'),
{ to: ['Email.address1@mailto.com', 'Email.address2@mailto.com'] },
'to multi-addresses test fail (separator ",")');
assert.deepEqual(queryURI('mailto:'),
{ to: [] },
'no to address test fail');
});
test('#cc', function() {
assert.deepEqual(queryURI('mailto:?cc=EmailCc.address@mailto.com'),
{ to: [], cc: ['EmailCc.address@mailto.com'] },
'cc single address test fail');
assert.deepEqual(queryURI('mailto:?cc=EmailCc.address1@mailto.com;' +
'EmailCc.address2@mailto.com;EmailCc.address3@mailto.com'),
{ to: [], cc: ['EmailCc.address1@mailto.com', 'EmailCc.address2@mailto.com',
'EmailCc.address3@mailto.com'] },
'cc multi-addresses test fail');
});
test('#bcc', function() {
assert.deepEqual(queryURI('mailto:?bcc=EmailBcc.address@mailto.com'),
{ to: [], bcc: ['EmailBcc.address@mailto.com'] },
'bcc single address test');
assert.deepEqual(queryURI('mailto:?bcc=EmailBcc.address1@mailto.com;' +
'EmailBcc.address2@mailto.com;EmailBcc.address3@mailto.com'),
{ to: [], bcc: ['EmailBcc.address1@mailto.com',
'EmailBcc.address2@mailto.com', 'EmailBcc.address3@mailto.com'] },
'bcc multi-addresses test fail');
});
test('#subject', function() {
assert.deepEqual(queryURI('mailto:?subject=This is the subject line'),
{ to: [], subject: 'This is the subject line' },
'subject test fail');
});
test('#body', function() {
assert.deepEqual(queryURI('mailto:?body=This is the body'),
{ to: [], body: 'This is the body' },
'body test fail');
});
test('#complex tests', function() {
assert.deepEqual(queryURI(
'mailto:Email.address1@mailto.com;Email.address2@mailto.com?' +
'cc=EmailCc1.address@mailto.com;EmailCc2.address@mailto.com&' +
'bcc=EmailBCc1.address@mailto.com;EmailBCc2.address@mailto.com'),
{
to: ['Email.address1@mailto.com', 'Email.address2@mailto.com'],
cc: ['EmailCc1.address@mailto.com', 'EmailCc2.address@mailto.com'],
bcc: ['EmailBCc1.address@mailto.com', 'EmailBCc2.address@mailto.com']
},
'complex 1 test fail');
assert.deepEqual(queryURI('mailto:Email.address1@mailto.com;?' +
'cc=EmailCc1.address@mailto.com;&bcc=EmailBCc1.address@mailto.com;' +
'&subject=This is the subject line&body=This is the text line one.' +
' %0AThis is the text line two'),
{
to: ['Email.address1@mailto.com'],
subject: 'This is the subject line',
body: 'This is the text line one. \nThis is the text line two',
cc: ['EmailCc1.address@mailto.com'],
bcc: ['EmailBCc1.address@mailto.com']
},
'complex 2 test fail');
});
});

View file

@ -0,0 +1,205 @@
'use strict';
/*global requireApp, suite, setup, testConfig, test, assert, suiteSetup,
suiteTeardown */
requireApp('email/js/alameda.js');
requireApp('email/test/config.js');
suite('message_list', function() {
var subject, modelCreate, HeaderCursor, MessageList;
var mockMessagesSlice = { items: [], die: function() {} };
var mockStarredMessage = {
isStarred: true
};
var mockReadMessage = {
isRead: true
};
var mockStarredReadMessage = {
isStarred: true,
isRead: true
};
var mockMessage = {
author: { name: 'author' },
subject: 'subject',
snippet: 'this is a body snippet',
date: Date.now(),
sendStatus: {}
};
var mockMatches = { author: { text: 'auth', matchRuns: [] } };
function testSelectedMessage(element, editMode, selected) {
assert.equal(element.getAttribute('aria-selected'),
editMode ? selected ? 'true' : 'false' : null);
assert.equal(element.querySelector('input[type=checkbox]').checked,
editMode && selected);
}
suiteSetup(function(done) {
testConfig({
suiteTeardown: suiteTeardown,
done: done
}, [
'model_create',
'header_cursor',
'element!cards/message_list_search',
'tmpl!cards/msg/header_item.html'], function(mc, hc, ml, hi) {
modelCreate = mc;
HeaderCursor = hc;
mockMessage.element = hi.cloneNode(true);
MessageList = ml;
});
});
setup(function() {
var headerCursor = new HeaderCursor(modelCreate.defaultModel);
headerCursor.messagesSlice = mockMessagesSlice;
subject = new MessageList();
subject.onArgs({
model: modelCreate.defaultModel,
headerCursor: headerCursor
});
});
suite('updateDomEditControls', function() {
test('no selected messages', function() {
subject.selectedMessages = [];
subject.updateDomEditControls();
assert.equal(subject.editToolbar.starBtn.getAttribute('data-l10n-id'),
'message-star-button');
assert.equal(subject.editToolbar.readBtn.getAttribute('data-l10n-id'),
'message-mark-read-button');
});
test('one read message', function() {
subject.selectedMessages = [Object.create(mockReadMessage)];
subject.updateDomEditControls();
assert.equal(subject.editToolbar.starBtn.getAttribute('data-l10n-id'),
'message-star-button');
assert.equal(subject.editToolbar.readBtn.getAttribute('data-l10n-id'),
'message-mark-unread-button');
});
test('one starred message', function() {
subject.selectedMessages = [Object.create(mockStarredMessage)];
subject.updateDomEditControls();
assert.equal(subject.editToolbar.starBtn.getAttribute('data-l10n-id'),
'message-unstar-button');
assert.equal(subject.editToolbar.readBtn.getAttribute('data-l10n-id'),
'message-mark-read-button');
});
test('one read and starred message', function() {
subject.selectedMessages = [Object.create(mockStarredReadMessage)];
subject.updateDomEditControls();
assert.equal(subject.editToolbar.starBtn.getAttribute('data-l10n-id'),
'message-unstar-button');
assert.equal(subject.editToolbar.readBtn.getAttribute('data-l10n-id'),
'message-mark-unread-button');
});
test('one read and one unread message', function() {
subject.selectedMessages = [Object.create(mockReadMessage),
Object.create(mockMessage)];
subject.updateDomEditControls();
assert.equal(subject.editToolbar.starBtn.getAttribute('data-l10n-id'),
'message-star-button');
assert.equal(subject.editToolbar.readBtn.getAttribute('data-l10n-id'),
'message-mark-unread-button');
});
test('one starred and one unstarred message', function() {
subject.selectedMessages = [Object.create(mockStarredMessage),
Object.create(mockMessage)];
subject.updateDomEditControls();
assert.equal(subject.editToolbar.starBtn.getAttribute('data-l10n-id'),
'message-star-button');
assert.equal(subject.editToolbar.readBtn.getAttribute('data-l10n-id'),
'message-mark-read-button');
});
});
suite('updateDomMessageChecked', function() {
var element;
setup(function() {
element = Object.create(mockMessage).element;
});
test('set checked', function() {
subject.updateDomMessageChecked(element, true);
testSelectedMessage(element, true, true);
});
test('set not checked', function() {
subject.updateDomMessageChecked(element, false);
testSelectedMessage(element, true, false);
});
});
suite('updateDomSelectState', function() {
var message, element;
setup(function() {
message = Object.create(mockMessage);
element = message.element;
subject.selectedMessages = [];
subject.editMode = true;
});
test('not in edit mode', function() {
subject.editMode = false;
subject.updateDomSelectState(element, message);
testSelectedMessage(element, false);
});
test('set selected', function() {
subject.selectedMessages.push(message);
subject.updateDomSelectState(element, message);
testSelectedMessage(element, true, true);
});
test('set not selected', function() {
subject.selectedMessages.pop(message);
subject.updateDomSelectState(element, message);
testSelectedMessage(element, true, false);
});
});
suite('updateMatchedMessageDom', function() {
var message, element;
setup(function() {
message = Object.create(mockMessage);
element = message.element;
subject.editMode = true;
subject.selectedMessages = [];
});
test('not edit mode', function() {
subject.editMode = false;
subject.updateMatchedMessageDom({
element: element,
header: message,
matches: mockMatches
});
testSelectedMessage(element, false);
});
test('message selected', function() {
subject.selectedMessages.push(message);
subject.updateMatchedMessageDom({
element: element,
header: message,
matches: mockMatches
});
testSelectedMessage(element, true, true);
});
test('message not selected', function() {
subject.selectedMessages.pop(message);
subject.updateMatchedMessageDom({
element: element,
header: message,
matches: mockMatches
});
testSelectedMessage(element, true, false);
});
});
});

View file

@ -0,0 +1,162 @@
'use strict';
/*global requireApp, suite, setup, testConfig, test, assert, suiteSetup,
suiteTeardown */
requireApp('email/js/alameda.js');
requireApp('email/test/config.js');
suite('message_reader', function() {
var subject, MessageReader, evt;
function MockMailHeader() {}
MockMailHeader.prototype = {
makeCopy: function() {
var copy = new MockMailHeader();
copy.isRead = this.isRead;
copy.isStarred = this.isStarred;
return copy;
},
setRead: function(isRead) { this.isRead = isRead; },
isRead: false,
isStarred: false,
setStarred: function() {}
};
var mockBody = {
bodyReps: [],
attachments: [{
isDownloaded: false,
isDownloadable: true,
sizeEstimateInBytes: 1024,
mimetype: '',
filename: ''
}]
};
suiteSetup(function(done) {
testConfig({
suiteTeardown: suiteTeardown,
done: done
}, ['evt', 'element!cards/message_reader'], function(e, mr) {
evt = e;
MessageReader = mr;
});
});
setup(function() {
subject = new MessageReader();
});
suite('_setHeader', function() {
var header;
setup(function() {
header = new MockMailHeader();
subject.readBtn.removeAttribute('data-l10n-id');
});
test('isRead', function() {
header.isRead = true;
subject._setHeader(header);
assert.equal(subject.readBtn.getAttribute('data-l10n-id'),
'message-mark-read-button');
});
test('!isRead', function() {
subject._setHeader(header);
assert.isNull(subject.readBtn.getAttribute('data-l10n-id'));
});
test('isStarred', function() {
header.isStarred = true;
subject._setHeader(header);
assert.equal(subject.starBtn.getAttribute('aria-pressed'), 'true');
});
test('!isStarred', function() {
subject._setHeader(header);
assert.equal(subject.starBtn.getAttribute('aria-pressed'), 'false');
});
});
suite('onToggleStar', function() {
setup(function() {
subject.hackMutationHeader = new MockMailHeader();
subject.header = new MockMailHeader();
});
test('isStarred', function() {
subject.onToggleStar();
assert.isTrue(subject.starBtn.classList.contains('msg-star-btn-on'));
assert.equal(subject.starBtn.getAttribute('aria-pressed'), 'true');
});
test('!isStarred', function() {
subject.hackMutationHeader.isStarred = true;
subject.onToggleStar();
assert.isFalse(subject.starBtn.classList.contains('msg-star-btn-on'));
assert.equal(subject.starBtn.getAttribute('aria-pressed'), 'false');
});
});
suite('setRead', function() {
setup(function() {
subject.hackMutationHeader = new MockMailHeader();
subject.header = new MockMailHeader();
});
test('isRead', function() {
subject.setRead(true);
assert.equal(subject.readBtn.getAttribute('data-l10n-id'),
'message-mark-read-button');
});
test('!isRead', function() {
subject.setRead(false);
assert.equal(subject.readBtn.getAttribute('data-l10n-id'),
'message-mark-unread-button');
});
});
suite('clearDom', function() {
var mockMessageContent = '<div>Message content</div>';
setup(function() {
subject.rootBodyNode.innerHTML = mockMessageContent;
assert.equal(subject.rootBodyNode.innerHTML, mockMessageContent);
assert.isNull(subject.rootBodyNode.querySelector('progress'));
});
test('test progress l10n', function() {
subject.clearDom();
var progress = subject.rootBodyNode.querySelector('progress');
assert.ok(progress);
assert.equal(progress.getAttribute('data-l10n-id'),
'message-body-container-progress');
});
});
suite('buildBodyDom', function() {
setup(function() {
subject.body = mockBody;
subject.header = new MockMailHeader();
});
test('disabled attachments accessibility', function(done) {
subject.body.attachments[0].isDownloadable = false;
evt.once('metrics:contentDone', function() {
assert.equal(subject.attachmentsContainer.children[0].getAttribute(
'aria-disabled'), 'true');
done();
});
subject.buildBodyDom();
});
test('attachments accessibility', function(done) {
subject.body.attachments[0].isDownloadable = true;
subject.body.attachments[0].isDownloaded = true;
evt.once('metrics:contentDone', function() {
assert.equal(subject.attachmentsContainer.children[0].getAttribute(
'aria-disabled'), 'false');
done();
});
subject.buildBodyDom();
});
});
});

View file

@ -0,0 +1,283 @@
'use strict';
/*global requireApp, suite, setup, testConfig, test, assert, suiteSetup,
suiteTeardown */
requireApp('email/js/alameda.js');
requireApp('email/test/config.js');
suite('message_list', function() {
var subject, modelCreate, HeaderCursor, MessageList;
var mockMessagesSlice = { items: [], die: function() {} };
var mockStarredMessage = {
isStarred: true
};
var mockReadMessage = {
isRead: true
};
var mockStarredReadMessage = {
isStarred: true,
isRead: true
};
var mockMessage = {
author: { name: 'author' },
subject: 'subject',
snippet: 'this is a body snippet',
date: Date.now(),
sendStatus: {}
};
function testRefreshBtnAccessibility(syncing) {
assert.equal(subject.refreshBtn.getAttribute('data-l10n-id'),
syncing ? 'messages-refresh-progress' : 'messages-refresh-button');
if (syncing) {
assert.equal(subject.refreshBtn.getAttribute('role'), 'progressbar');
} else {
assert.isNull(subject.refreshBtn.getAttribute('role'));
}
}
function testSelectedMessage(element, editMode, selected) {
assert.equal(element.getAttribute('aria-selected'),
editMode ? selected ? 'true' : 'false' : null);
assert.equal(element.querySelector('input[type=checkbox]').checked,
editMode && selected);
}
function testMessageState(message, state) {
message.sendStatus.state = state;
subject.updateMessageDom(message);
var syncingNode = message.element
.querySelector('.msg-header-syncing-section');
if (state) {
assert.equal(syncingNode.getAttribute('data-l10n-id'),
'message-header-state-' + state);
} else {
assert.equal(true, !syncingNode.hasAttribute('data-l10n-id'));
}
}
suiteSetup(function(done) {
testConfig({
suiteTeardown: suiteTeardown,
done: done
}, [
'model_create',
'header_cursor',
'element!cards/message_list',
'tmpl!cards/msg/header_item.html'], function(mc, hc, ml, hi) {
modelCreate = mc;
HeaderCursor = hc;
mockMessage.element = hi.cloneNode(true);
MessageList = ml;
});
});
setup(function() {
var headerCursor = new HeaderCursor(modelCreate.defaultModel);
headerCursor.messagesSlice = mockMessagesSlice;
subject = new MessageList();
subject.onArgs({
model: modelCreate.defaultModel,
headerCursor: headerCursor
});
});
suite('messages_status', function() {
setup(function() {
subject.curFolder = { type: 'inbox' };
});
test('synchronizing', function() {
testRefreshBtnAccessibility(false);
subject.msgVScroll.messages_status('synchronizing');
testRefreshBtnAccessibility(true);
});
test('synced', function() {
testRefreshBtnAccessibility(false);
subject.msgVScroll.messages_status('synced');
testRefreshBtnAccessibility(false);
});
});
suite('toggleOutboxSyncingDisplay', function() {
setup(function() {
subject.curFolder = { type: 'outbox' };
});
test('syncing', function() {
testRefreshBtnAccessibility(false);
subject.toggleOutboxSyncingDisplay(true);
testRefreshBtnAccessibility(true);
});
test('not syncing', function() {
setup(function() {
subject._outboxSyncing = true;
});
testRefreshBtnAccessibility(false);
subject.toggleOutboxSyncingDisplay(false);
testRefreshBtnAccessibility(false);
});
});
suite('updateDomEditControls', function() {
test('no selected messages', function() {
subject.selectedMessages = [];
subject.updateDomEditControls();
assert.equal(subject.editToolbar.starBtn.getAttribute('data-l10n-id'),
'message-star-button');
assert.equal(subject.editToolbar.readBtn.getAttribute('data-l10n-id'),
'message-mark-read-button');
});
test('one read message', function() {
subject.selectedMessages = [Object.create(mockReadMessage)];
subject.updateDomEditControls();
assert.equal(subject.editToolbar.starBtn.getAttribute('data-l10n-id'),
'message-star-button');
assert.equal(subject.editToolbar.readBtn.getAttribute('data-l10n-id'),
'message-mark-unread-button');
});
test('one starred message', function() {
subject.selectedMessages = [Object.create(mockStarredMessage)];
subject.updateDomEditControls();
assert.equal(subject.editToolbar.starBtn.getAttribute('data-l10n-id'),
'message-unstar-button');
assert.equal(subject.editToolbar.readBtn.getAttribute('data-l10n-id'),
'message-mark-read-button');
});
test('one read and starred message', function() {
subject.selectedMessages = [Object.create(mockStarredReadMessage)];
subject.updateDomEditControls();
assert.equal(subject.editToolbar.starBtn.getAttribute('data-l10n-id'),
'message-unstar-button');
assert.equal(subject.editToolbar.readBtn.getAttribute('data-l10n-id'),
'message-mark-unread-button');
});
test('one read and one unread message', function() {
subject.selectedMessages = [Object.create(mockReadMessage),
Object.create(mockMessage)];
subject.updateDomEditControls();
assert.equal(subject.editToolbar.starBtn.getAttribute('data-l10n-id'),
'message-star-button');
assert.equal(subject.editToolbar.readBtn.getAttribute('data-l10n-id'),
'message-mark-unread-button');
});
test('one starred and one unstarred message', function() {
subject.selectedMessages = [Object.create(mockStarredMessage),
Object.create(mockMessage)];
subject.updateDomEditControls();
assert.equal(subject.editToolbar.starBtn.getAttribute('data-l10n-id'),
'message-star-button');
assert.equal(subject.editToolbar.readBtn.getAttribute('data-l10n-id'),
'message-mark-read-button');
});
});
suite('updateDomMessageChecked', function() {
var element;
setup(function() {
element = Object.create(mockMessage).element;
});
test('set checked', function() {
subject.updateDomMessageChecked(element, true);
testSelectedMessage(element, true, true);
});
test('set not checked', function() {
subject.updateDomMessageChecked(element, false);
testSelectedMessage(element, true, false);
});
});
suite('updateDomSelectState', function() {
var message, element;
setup(function() {
message = Object.create(mockMessage);
element = message.element;
subject.selectedMessages = [];
subject.editMode = true;
});
test('not in edit mode', function() {
subject.editMode = false;
subject.updateDomSelectState(element, message);
testSelectedMessage(element, false);
});
test('set selected', function() {
subject.selectedMessages.push(message);
subject.updateDomSelectState(element, message);
testSelectedMessage(element, true, true);
});
test('set not selected', function() {
subject.selectedMessages.pop(message);
subject.updateDomSelectState(element, message);
testSelectedMessage(element, true, false);
});
});
suite('_setEditMode', function() {
var element;
setup(function() {
var message = Object.create(mockMessage);
subject.updateMessageDom(message);
element = message.element;
assert.isNull(element.getAttribute('aria-selected'));
subject.msgVScroll.appendChild(element);
});
test('in editMode', function() {
subject._setEditMode(true);
testSelectedMessage(element, true, false);
});
});
suite('updateMessageDom', function() {
var message, element;
setup(function() {
message = Object.create(mockMessage);
element = message.element;
subject.editMode = true;
subject.selectedMessages = [];
});
test('not edit mode', function() {
subject.editMode = false;
subject.updateMessageDom(message);
testSelectedMessage(element, false);
});
test('message selected', function() {
subject.selectedMessages.push(message);
subject.updateMessageDom(message);
testSelectedMessage(element, true, true);
});
test('message not selected', function() {
subject.selectedMessages.pop(message);
subject.updateMessageDom(message);
testSelectedMessage(element, true, false);
});
test('message has a sending state', function() {
testMessageState(message, 'sending');
});
test('message has an error state', function() {
testMessageState(message, 'error');
});
test('message has no state', function() {
testMessageState(message);
});
});
});

View file

@ -0,0 +1,82 @@
/*jshint browser: true */
/*global define */
'use strict';
define(function() {
var api;
api = {
setInteractive: function() {},
useLocalizedStrings: function() {},
viewAccounts: function() {
var account = {
id: 'fake_account',
name: 'fake account'
};
var acctsSlice = {
items: [
account
],
defaultAccount: account,
die: function() {}
};
setTimeout(function() {
if (!acctsSlice.oncomplete) {
return;
}
acctsSlice.oncomplete();
});
return acctsSlice;
},
viewFolderMessages: function() {
var messagesSlice = {
items: [],
die: function() {}
};
return messagesSlice;
},
viewFolders: function(mode, argument) {
var inboxFolder = {
id: 'fake_inbox',
type: 'inbox',
name: 'inbox'
};
var foldersSlice = {
items: [
inboxFolder
],
getFirstFolderWithType: function(type) {
if (type !== 'inbox') {
throw new Error('Only type of inbox supported in mock_api');
}
return inboxFolder;
},
die: function() {}
};
setTimeout(function() {
if (!foldersSlice.oncomplete) {
return;
}
foldersSlice.oncomplete();
});
return foldersSlice;
}
};
return api;
});

View file

@ -0,0 +1,49 @@
'use strict';
/*jshint browser: true */
/*global requireApp, suite, testConfig, test, assert,
suiteSetup, suiteTeardown */
requireApp('email/js/alameda.js');
requireApp('email/test/config.js');
suite('model acctsSlice, startup', function() {
var api, model;
suiteSetup(function(done) {
testConfig(
{
suiteTeardown: suiteTeardown,
done: done
},
['api', 'model_create'],
function(a, mc) {
api = a;
model = mc.defaultModel;
}
);
});
test('consistent view of acctsSlice initialization', function(done) {
var fakeSlice = {
oncomplete: function() { },
items: [],
die: function() { }
};
sinon.stub(api, 'viewAccounts').returns(fakeSlice);
// Wait for model.init to finish this tick...
setTimeout(function() {
assert.ok(!model.acctsSlice,
'model.acctsSlice should not be set ' +
'before acctsSlice.oncomplete fires');
fakeSlice.oncomplete();
assert.ok(model.acctsSlice,
'model.acctsSlice _should_ be set ' +
'after acctsSlice.oncomplete fires');
done();
});
model.init();
});
});

View file

@ -0,0 +1,53 @@
'use strict';
/*jshint browser: true */
/*global requireApp, suite, setup, testConfig, test, assert,
suiteSetup, suiteTeardown */
requireApp('email/js/alameda.js');
requireApp('email/test/config.js');
suite('model_create', function() {
var model;
suiteSetup(function(done) {
testConfig(
{
suiteTeardown: suiteTeardown,
done: done
},
['model_create'],
function(mc) {
model = mc.defaultModel;
}
);
});
suite('no account by default', function() {
test('hasAccount false', function() {
assert.equal(model.hasAccount(), false);
assert.equal(model.inited, false);
});
});
suite('load default account', function() {
var account, acctsSlice, foldersSlice;
setup(function(done) {
model.init();
model.latestOnce('foldersSlice', function() {
account = model.account;
acctsSlice = model.acctsSlice;
foldersSlice = model.foldersSlice;
done();
});
});
test('hasAccount true', function() {
assert.equal(model.hasAccount(), true);
assert.equal(model.inited, true);
assert.equal(acctsSlice.defaultAccount === account, true);
});
});
});

View file

@ -0,0 +1,75 @@
'use strict';
/*jshint browser: true */
/*global requireApp, suite, testConfig, test, assert,
suiteSetup, suiteTeardown */
requireApp('email/js/alameda.js');
requireApp('email/test/config.js');
suite('query_string', function() {
var queryString,
string1 = 'type=message_list&accountId=A',
object1 = {
type: 'message_list',
accountId: 'A'
},
string2 = 'type=message_reader&accountId=B&messageSuid=B%2F1',
object2 = {
type: 'message_reader',
accountId: 'B',
messageSuid: 'B/1'
};
suiteSetup(function(done) {
testConfig(
{
suiteTeardown: suiteTeardown,
done: done
},
['query_string'],
function(q) {
queryString = q;
}
);
});
suite('#toObject', function() {
test('basic use', function() {
var result1 = queryString.toObject(string1),
result2 = queryString.toObject(string2);
assert.equal('message_list', result1.type);
assert.equal('A', result1.accountId);
assert.equal('message_reader', result2.type);
assert.equal('B', result2.accountId);
assert.equal('B/1', result2.messageSuid);
});
});
suite('#fromObject', function() {
test('basic use', function() {
var result1 = queryString.fromObject(object1),
result2 = queryString.fromObject(object2);
assert.equal(string1, result1);
assert.equal(string2, result2);
});
});
suite('encodeURIComponent use', function() {
test('basic use', function() {
var result1 = queryString
.toObject('url%26type=http%3A%2F%2Fmozilla.com%2F');
assert.equal('http://mozilla.com/', result1['url&type']);
});
});
});

View file

@ -0,0 +1,102 @@
/*global requireApp, suite, setup, testConfig, test, assert,
suiteSetup, suiteTeardown, Event */
'use strict';
requireApp('email/js/alameda.js');
requireApp('email/test/config.js');
suite('IMAP protocol dropdown', function() {
var smc;
var SetupManualConfig;
var el;
suiteSetup(function(done) {
testConfig({
suiteTeardown: suiteTeardown,
done: done,
defines: {}
},
['element!cards/setup_manual_config'], function(smc) {
SetupManualConfig = smc;
}
);
});
setup(function() {
smc = new SetupManualConfig();
/*
el = document.createElement('div');
el.style.width = '100%';
el.style.height = '100%';
el.appendChild(smc);
var refNode = document.childNodes[0];
if (refNode) {
document.insertBefore(el, refNode);
} else {
document.appendChild(el);
}
*/
});
suite('IMAP port selection', function() {
// TEST DISABLED: see comments below, some weirdness with custom elements
// receiving the change event in this test.
test('should change upon selecting STARTTLS', function(done) {
var dropdown = smc.getElementsByClassName(
'sup-manual-composite-socket')[0];
var port = smc.getElementsByClassName('sup-manual-composite-port')[0];
assert.equal(port.value, '993');
assert.equal(dropdown.value, 'SSL');
/*
var oldFn = smc.onChangeCompositeSocket;
smc.onChangeCompositeSocket = function(event) {
oldFn.call(smc, event);
assert.equal(port.value, '143');
done();
};
*/
dropdown.value = 'STARTTLS';
// TODO: this event dispatch does not seem to actually trigger a change
// event on the dropdown element. See commented out code above. Theory:
// with a custom element, not really part of the DOM until inserted in the
// document, and then events would fire, but document.appendChild() fails
// in this test harness.
dropdown.dispatchEvent(new Event('change'));
assert.equal(port.value, '143');
done();
});
});
suite('SMTP port selection', function() {
// TEST DISABLED: see comments below, some weirdness with custom elements
// receiving the change event in this test.
test('should change upon selecting STARTTLS', function(done) {
var dropdown = smc.getElementsByClassName('sup-manual-smtp-socket')[0];
var port = smc.getElementsByClassName('sup-manual-smtp-port')[0];
assert.equal(port.value, '465');
assert.equal(dropdown.value, 'SSL');
/*
var oldFn = smc.onChangeSmtpSocket;
smc.onChangeSmtpSocket = function(event) {
oldFn.call(smc, event);
assert.equal(port.value, '587');
done();
};
*/
dropdown.value = 'STARTTLS';
// TODO: this event dispatch does not seem to actually trigger a change
// event on the dropdown element. See commented out code above. Theory:
// with a custom element, not really part of the DOM until inserted in the
// document, and then events would fire, but document.appendChild() fails
// in this test harness.
dropdown.dispatchEvent(new Event('change'));
assert.equal(port.value, '587');
done();
});
});
teardown(function() {
smc = null;
el = null;
});
});