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:
parent
93e3a48004
commit
b38dea0597
25901 changed files with 9982925 additions and 369 deletions
132
gaia/apps/sms/test/marionette/lib/views/conversation/accessors.js
Executable file
132
gaia/apps/sms/test/marionette/lib/views/conversation/accessors.js
Executable file
|
|
@ -0,0 +1,132 @@
|
|||
'use strict';
|
||||
|
||||
/* global module */
|
||||
|
||||
var SELECTORS = Object.freeze({
|
||||
main: '.panel-ConversationView',
|
||||
message: '.message',
|
||||
messageBubble: '.message .bubble',
|
||||
header: '#messages-header',
|
||||
headerTitle: '#messages-header-text',
|
||||
headerActionButton: '.action-button',
|
||||
container: '#messages-container',
|
||||
callButton: '#messages-call-number-button',
|
||||
// TODO: Remove the next selector once Message has its own accessors
|
||||
messageContent: '.message-content > p:first-child',
|
||||
createNewContactMenuOption: '.contact-prompt ' +
|
||||
'[data-l10n-id="createNewContact"]',
|
||||
addToExistingContactOption: '.contact-prompt ' +
|
||||
'[data-l10n-id="addToExistingContact"]',
|
||||
carrierHeaderPhoneNumber: '#contact-carrier .phone-number',
|
||||
optionsButton: '#messages-options-button'
|
||||
});
|
||||
|
||||
var EDIT_MODE_SELECTORS = Object.freeze({
|
||||
toggleSelectionButton: '#messages-check-uncheck-all-button',
|
||||
editHeaderTitle: '#messages-edit-mode'
|
||||
});
|
||||
|
||||
function ConversationAccessor(client) {
|
||||
this.client = client;
|
||||
this.actions = client.loader.getActions();
|
||||
}
|
||||
|
||||
ConversationAccessor.prototype = {
|
||||
get messages() {
|
||||
return this.client.findElements(SELECTORS.message);
|
||||
},
|
||||
|
||||
get message() {
|
||||
return this.client.helper.waitForElement(SELECTORS.messageBubble);
|
||||
},
|
||||
|
||||
get header() {
|
||||
return this.client.helper.waitForElement(SELECTORS.header);
|
||||
},
|
||||
|
||||
get headerTitle() {
|
||||
return this.client.helper.waitForElement(SELECTORS.headerTitle);
|
||||
},
|
||||
|
||||
get editHeaderTitle() {
|
||||
return this.client.helper.waitForElement(
|
||||
EDIT_MODE_SELECTORS.editHeaderTitle
|
||||
);
|
||||
},
|
||||
|
||||
get headerActionButton() {
|
||||
return this.client.helper.waitForElement(SELECTORS.headerActionButton);
|
||||
},
|
||||
|
||||
get carrierHeaderPhoneNumber() {
|
||||
return this.client.helper.waitForElement(
|
||||
SELECTORS.carrierHeaderPhoneNumber
|
||||
);
|
||||
},
|
||||
|
||||
get callButton() {
|
||||
return this.client.helper.waitForElement(SELECTORS.callButton);
|
||||
},
|
||||
|
||||
get optionsButton() {
|
||||
return this.client.helper.waitForElement(SELECTORS.optionsButton);
|
||||
},
|
||||
|
||||
get toggleSelectionButton() {
|
||||
return this.client.helper.waitForElement(
|
||||
EDIT_MODE_SELECTORS.toggleSelectionButton
|
||||
);
|
||||
},
|
||||
|
||||
get createNewContactOption() {
|
||||
return this.client.helper.waitForElement(
|
||||
SELECTORS.createNewContactMenuOption
|
||||
);
|
||||
},
|
||||
|
||||
get addToExistingContactOption() {
|
||||
return this.client.helper.waitForElement(
|
||||
SELECTORS.addToExistingContactOption
|
||||
);
|
||||
},
|
||||
|
||||
getMessageContent: function(message) {
|
||||
return this.client.helper.waitForElement(
|
||||
message.findElement(SELECTORS.messageContent)
|
||||
);
|
||||
},
|
||||
|
||||
findMessage: function(id) {
|
||||
return this.client.findElement(
|
||||
'.message[data-message-id="' + id + '"] .bubble'
|
||||
);
|
||||
},
|
||||
|
||||
scrollUp: function() {
|
||||
var conversationContainer = this.client.findElement(
|
||||
SELECTORS.container
|
||||
);
|
||||
|
||||
this.actions.flick(conversationContainer, 50, 50, 50, 350).perform();
|
||||
},
|
||||
|
||||
fakeScrollUpTo: function(scrollTop) {
|
||||
var conversationContainer = this.client.findElement(
|
||||
SELECTORS.container
|
||||
);
|
||||
|
||||
conversationContainer.scriptWith(function(container, scrollTop) {
|
||||
container.scrollTop = scrollTop;
|
||||
}, [scrollTop]);
|
||||
},
|
||||
|
||||
waitToAppear: function() {
|
||||
var conversationPanel = this.client.helper.waitForElement(SELECTORS.main);
|
||||
this.client.waitFor(function() {
|
||||
return conversationPanel.location().x === 0;
|
||||
});
|
||||
return conversationPanel;
|
||||
}
|
||||
};
|
||||
|
||||
module.exports = ConversationAccessor;
|
||||
72
gaia/apps/sms/test/marionette/lib/views/conversation/message_accessors.js
Executable file
72
gaia/apps/sms/test/marionette/lib/views/conversation/message_accessors.js
Executable file
|
|
@ -0,0 +1,72 @@
|
|||
'use strict';
|
||||
|
||||
/* global module */
|
||||
|
||||
var SELECTORS = Object.freeze({
|
||||
main: '.message[data-message-id="{id}"]',
|
||||
bubble: '.bubble',
|
||||
content: '.message-content-body',
|
||||
attachment: '.attachment-container',
|
||||
downloadButton: 'button.download'
|
||||
});
|
||||
|
||||
function MessageAccessor(client) {
|
||||
this.client = client.scope({ searchTimeout: 50 });
|
||||
this.actions = client.loader.getActions();
|
||||
}
|
||||
|
||||
MessageAccessor.prototype = {
|
||||
find: function(messageId) {
|
||||
return this.client.findElement(SELECTORS.main.replace('{id}', messageId));
|
||||
},
|
||||
|
||||
parse: function(messageNode) {
|
||||
// It's workaround for the case when message node reference is stale,
|
||||
// so when we try to access MarionetteElement from the node that's not
|
||||
// attached to the dom we'll get an exception.
|
||||
try {
|
||||
var messageClass = messageNode.getAttribute('class');
|
||||
var checkbox = messageNode.findElement('input[type=checkbox]');
|
||||
// Since most of the messages don't contain any attachments, let's make
|
||||
// it much faster.
|
||||
var attachments = this.client.scope({ searchTimeout: 50 }).findElements(
|
||||
SELECTORS.attachment, null /* default search method */, messageNode.id
|
||||
);
|
||||
|
||||
return {
|
||||
id: +messageNode.getAttribute('data-message-id'),
|
||||
type: messageClass.indexOf('mms') >= 0 ? 'mms' : 'sms',
|
||||
content: messageNode.findElement(SELECTORS.content).text(),
|
||||
attachments: attachments.map(function(attachment) {
|
||||
return {
|
||||
screenshot: function() {
|
||||
return this.client.screenshot({ element: attachment });
|
||||
}.bind(this),
|
||||
type: attachment.getAttribute('data-attachment-type')
|
||||
};
|
||||
}, this),
|
||||
isDownloaded: messageClass.indexOf('not-downloaded') < 0,
|
||||
isPending: messageClass.indexOf('pending') >= 0,
|
||||
isInEditMode: messageNode.findElement('.pack-checkbox').displayed(),
|
||||
isSelected: checkbox.getAttribute('checked') === 'true',
|
||||
isDisplayed: messageNode.displayed()
|
||||
};
|
||||
} catch(e) {
|
||||
console.warn('Message node is not available', e, e.stack);
|
||||
|
||||
return null;
|
||||
}
|
||||
},
|
||||
|
||||
download: function(messageId) {
|
||||
var messageNode = this.find(messageId);
|
||||
|
||||
if (this.parse(messageNode).isDownloaded) {
|
||||
throw new Error('Message has been already downloaded!');
|
||||
}
|
||||
|
||||
messageNode.findElement(SELECTORS.downloadButton).tap();
|
||||
}
|
||||
};
|
||||
|
||||
module.exports = MessageAccessor;
|
||||
287
gaia/apps/sms/test/marionette/lib/views/conversation/view.js
Executable file
287
gaia/apps/sms/test/marionette/lib/views/conversation/view.js
Executable file
|
|
@ -0,0 +1,287 @@
|
|||
'use strict';
|
||||
|
||||
/* global module */
|
||||
|
||||
var ConversationAccessor = require('./accessors');
|
||||
var MessageAccessor = require('./message_accessors');
|
||||
var ComposerAccessor = require('../shared/composer_accessors');
|
||||
var MenuAccessor = require('../shared/menu_accessors');
|
||||
var Tools = require('../shared/tools.js');
|
||||
|
||||
var appRoot = require('app-root-path');
|
||||
// TODO Change the path once requireFromApp becomes its own module
|
||||
var fromApp = require(appRoot + '/shared/test/integration/require_from_app');
|
||||
var Contacts = fromApp('contacts').require('lib/contacts');
|
||||
|
||||
function ConversationView(client) {
|
||||
this.client = client;
|
||||
this.accessors = new ConversationAccessor(client);
|
||||
this.messageAccessors = new MessageAccessor(client);
|
||||
this.composerAccessors = new ComposerAccessor(client);
|
||||
this.menuAccessors = new MenuAccessor(client);
|
||||
}
|
||||
|
||||
ConversationView.prototype = {
|
||||
// TODO Move these constants to marionette, see bug 1207516
|
||||
KEYS: {
|
||||
backspace: '\ue003'
|
||||
},
|
||||
|
||||
get headerTitle() {
|
||||
return this.accessors.headerTitle.text();
|
||||
},
|
||||
|
||||
get editHeaderTitle() {
|
||||
return this.accessors.editHeaderTitle.text();
|
||||
},
|
||||
|
||||
get toggleSelectionButtonTitle() {
|
||||
return this.accessors.toggleSelectionButton.text();
|
||||
},
|
||||
|
||||
/**
|
||||
* Returns the messages in this conversation, following an optional filter.
|
||||
* @param {Object} [filter] Optional filter
|
||||
* @param {Number} [filter.first] Will return the first n messages.
|
||||
* @returns {ParsedMessage[]} List of the messages found.
|
||||
*/
|
||||
messages: function(filter) {
|
||||
filter = filter || [];
|
||||
var messages = this.accessors.messages;
|
||||
|
||||
if (filter.first) {
|
||||
messages = messages.slice(0, filter.first);
|
||||
}
|
||||
|
||||
return messages.map(function(message) {
|
||||
return this.messageAccessors.parse(message);
|
||||
}, this);
|
||||
},
|
||||
|
||||
get carrierHeaderPhoneNumber() {
|
||||
return this.accessors.carrierHeaderPhoneNumber.text();
|
||||
},
|
||||
|
||||
findMessage: function(messageId) {
|
||||
var messageNode = this.messageAccessors.find(messageId);
|
||||
return messageNode && this.messageAccessors.parse(messageNode);
|
||||
},
|
||||
|
||||
waitForFullRendering: function(thread) {
|
||||
var lastId = thread.messages[thread.messages.length - 1].id;
|
||||
this.accessors.findMessage(lastId);
|
||||
},
|
||||
|
||||
downloadMessage: function(messageId) {
|
||||
this.messageAccessors.download(messageId);
|
||||
},
|
||||
|
||||
get attachments() {
|
||||
return this.composerAccessors.attachments;
|
||||
},
|
||||
|
||||
get messageText() {
|
||||
return this.composerAccessors.messageInput.text().trim();
|
||||
},
|
||||
|
||||
get subject() {
|
||||
return this.composerAccessors.subjectInput.text().trim();
|
||||
},
|
||||
|
||||
back: function() {
|
||||
this.client.switchToShadowRoot(this.accessors.header);
|
||||
this.accessors.headerActionButton.tap();
|
||||
this.client.switchToShadowRoot();
|
||||
},
|
||||
|
||||
callContact: function() {
|
||||
this.accessors.callButton.tap();
|
||||
|
||||
var Dialer = fromApp('dialer').require('lib/dialer');
|
||||
var dialer = new Dialer(this.client);
|
||||
dialer.switchTo();
|
||||
return dialer;
|
||||
},
|
||||
|
||||
openCreateNewContact: function() {
|
||||
this.accessors.headerTitle.tap();
|
||||
this.accessors.createNewContactOption.tap();
|
||||
var contacts = new Contacts(this.client);
|
||||
contacts.switchToCreateNewContactActivity();
|
||||
return contacts;
|
||||
},
|
||||
|
||||
openAddToExistingContact: function() {
|
||||
this.accessors.headerTitle.tap();
|
||||
this.accessors.addToExistingContactOption.tap();
|
||||
return this._switchToContactsApp();
|
||||
},
|
||||
|
||||
openParticipants: function() {
|
||||
this.accessors.headerTitle.tap();
|
||||
|
||||
var ParticipantsView = require('../participants/view');
|
||||
var participantsView = new ParticipantsView(this.client);
|
||||
participantsView.accessors.waitToAppear();
|
||||
return participantsView;
|
||||
},
|
||||
|
||||
openReport: function(messageId) {
|
||||
var message = this.accessors.findMessage(messageId);
|
||||
|
||||
// Show context menu on message bubble.
|
||||
this.client.loader.getActions().longPress(message, 1 /* sec */).perform();
|
||||
this.menuAccessors.selectAppMenuOption('View message report');
|
||||
|
||||
var ReportView = require('../report/view');
|
||||
var reportView = new ReportView(this.client, this);
|
||||
reportView.accessors.waitToAppear();
|
||||
return reportView;
|
||||
},
|
||||
|
||||
deleteMessage: function(messageId) {
|
||||
var lastMessageWillBeDeleted = this.messages().length === 1;
|
||||
|
||||
var message = this.accessors.findMessage(messageId);
|
||||
|
||||
// Show context menu on message bubble.
|
||||
this.client.loader.getActions().longPress(message, 1 /* sec */).perform();
|
||||
this.menuAccessors.selectAppMenuOption('Delete');
|
||||
|
||||
var dialogView = new (require('../dialog/view'))(this.client);
|
||||
dialogView.chooseAction('Delete');
|
||||
|
||||
// If deleted message was the last one, user will be returned back to Inbox.
|
||||
var currentView;
|
||||
if (lastMessageWillBeDeleted) {
|
||||
currentView = new (require('../inbox/view'))(this.client);
|
||||
currentView.accessors.waitToAppear();
|
||||
} else {
|
||||
currentView = this;
|
||||
}
|
||||
|
||||
return currentView;
|
||||
},
|
||||
|
||||
clearMessage: function() {
|
||||
var messageInput = this.composerAccessors.messageInput;
|
||||
messageInput.tap();
|
||||
while (messageInput.text() !== '') {
|
||||
messageInput.sendKeys(this.KEYS.backspace);
|
||||
}
|
||||
},
|
||||
|
||||
typeMessage: function(message) {
|
||||
this.composerAccessors.messageInput.sendKeys(message);
|
||||
},
|
||||
|
||||
addAttachment: function() {
|
||||
return this.composerAccessors.addAttachment();
|
||||
},
|
||||
|
||||
/**
|
||||
* Take a screenshot of an attachment thumbnail in the composer.
|
||||
* @param {Number} The attachment index
|
||||
* @return {String} The screenshot
|
||||
*/
|
||||
takeComposerAttachmentScreenshot: function(index) {
|
||||
return this.composerAccessors.takeAttachmentScreenshot(index);
|
||||
},
|
||||
|
||||
typeSubject: function(subject) {
|
||||
this.composerAccessors.subjectInput.sendKeys(subject);
|
||||
},
|
||||
|
||||
showOptions: function() {
|
||||
this.accessors.optionsButton.tap();
|
||||
},
|
||||
|
||||
showSubject: function() {
|
||||
this.showOptions();
|
||||
this.menuAccessors.selectAppMenuOption('Add subject');
|
||||
this.client.helper.waitForElement(this.composerAccessors.subjectInput);
|
||||
},
|
||||
|
||||
/**
|
||||
* Launch the "include someone else" action.
|
||||
* @returns {NewMessageView} the new message view we navigated to.
|
||||
*/
|
||||
includeSomeoneElse: function() {
|
||||
this.showOptions();
|
||||
this.menuAccessors.selectAppMenuOption('Include someone else');
|
||||
|
||||
var NewMessageView = require('../new-message/view');
|
||||
var newMessageView = new NewMessageView(this.client);
|
||||
newMessageView.accessors.waitToAppear();
|
||||
return newMessageView;
|
||||
},
|
||||
|
||||
hideSubject: function() {
|
||||
this.showOptions();
|
||||
this.menuAccessors.selectAppMenuOption('Remove subject');
|
||||
this.client.helper.waitForElementToDisappear(
|
||||
this.composerAccessors.subjectInput
|
||||
);
|
||||
},
|
||||
|
||||
enterEditMode: function() {
|
||||
this.showOptions();
|
||||
this.menuAccessors.selectAppMenuOption('Select Messages');
|
||||
},
|
||||
|
||||
exitEditMode: function() {
|
||||
this.back();
|
||||
},
|
||||
|
||||
tapOnMessage: function(messageId) {
|
||||
var messageNode = this.messageAccessors.find(messageId);
|
||||
messageNode.scriptWith(function(node) {
|
||||
node.scrollIntoView();
|
||||
});
|
||||
messageNode.tap();
|
||||
},
|
||||
|
||||
toggleMessagesSelection: function() {
|
||||
var header = this.accessors.header;
|
||||
// This is a workaround needed as the screen is moving app is
|
||||
// moving outside the screen on running tapOnMessage().
|
||||
// This is needed to get the header back in the frame
|
||||
header.scriptWith(function(node) {
|
||||
node.scrollIntoView();
|
||||
});
|
||||
this.accessors.toggleSelectionButton.tap();
|
||||
},
|
||||
|
||||
isSubjectVisible: function() {
|
||||
var subjectInput = this.composerAccessors.subjectInput;
|
||||
return subjectInput && this.composerAccessors.subjectInput.displayed();
|
||||
},
|
||||
|
||||
fakeScrollUpTo: function(scrollTop) {
|
||||
this.accessors.fakeScrollUpTo(scrollTop);
|
||||
},
|
||||
|
||||
assertMessageInputFocused: function(message) {
|
||||
Tools.assertElementFocused(this.composerAccessors.messageInput, message);
|
||||
},
|
||||
|
||||
backToInbox: function() {
|
||||
this.back();
|
||||
|
||||
var InboxView = require('../inbox/view');
|
||||
var inboxView = new InboxView(this.client);
|
||||
inboxView.accessors.waitToAppear();
|
||||
|
||||
return inboxView;
|
||||
},
|
||||
|
||||
_switchToContactsApp: function() {
|
||||
var contacts = new Contacts(this.client);
|
||||
contacts.switchToApp();
|
||||
return contacts;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
||||
module.exports = ConversationView;
|
||||
Loading…
Reference in a new issue