VulpesOS/gaia/apps/verticalhome/test/marionette/lib/verticalhome.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

354 lines
9.6 KiB
JavaScript
Executable file

'use strict';
var getIconId = require('./icon_id');
/**
* Abstraction around homescreen.
* @constructor
*/
function VerticalHome(client) {
this.client = client;
this.system = client.loader.getAppClass('system');
}
/**
* @type String Origin of VerticalHome app
*/
VerticalHome.URL = 'app://verticalhome.gaiamobile.org';
VerticalHome.Selectors = {
grid: '#icons',
editHeaderText: '#edit-header h1',
editHeaderDone: '#exit-edit-mode',
editGroup: '#edit-group',
editGroupTitle: '#edit-group-title',
editGroupSave: '#edit-group-save',
editGroupTitleClear: '#edit-group-title-clear',
search: '#search',
firstIcon: '#icons div.icon:not(.placeholder)',
groupHeader: '#icons .group .header',
groupTitle: '#icons .group .header .title',
groupBackground: '#icons .group .background',
groupToggle: '#icons .group .toggle',
dividers: '#icons section.divider',
contextmenu: '#contextmenu-dialog',
themeColor: 'head meta[name="theme-color"]',
placeholders: '#icons .placeholder'
};
/**
* Launches our new homescreen and focuses on it.
*/
VerticalHome.prototype = {
URL: VerticalHome.URL,
Selectors: VerticalHome.Selectors,
get numIcons() {
return this.client.findElements(VerticalHome.Selectors.firstIcon).length;
},
get numDividers() {
return this.dividers.length;
},
get dividers() {
return this.client.findElements(VerticalHome.Selectors.dividers);
},
get contextMenu() {
return this.client.findElement(VerticalHome.Selectors.contextmenu);
},
/**
Fetch a particular type of a gaia-confirm dialog.
@param {String} type of dialog.
*/
getConfirmDialog: function(type) {
var selector = 'gaia-confirm[data-type="' + type + '"]';
return this.client.helper.waitForElement(selector);
},
/**
Click confirm on a particular type of confirmation dialog.
@param {String} type of dialog.
@param {String} selector of the button. Defaults to .confirm.
*/
confirmDialog: function(type, button) {
var dialog = this.getConfirmDialog(type);
var confirm;
this.client.waitFor(function() {
confirm = dialog.findElement(button || '.confirm');
return confirm && confirm.displayed();
});
// XXX: Hack to use faster polling
var quickly = this.client.scope({ searchTimeout: 50 });
confirm.client = quickly;
// tricky logic to ensure the dialog has been removed and clicked
this.client.waitFor(function() {
try {
// click the dialog to dismiss it
confirm.click();
// ensure it is either hidden or hits the stale element ref
return !confirm.displayed();
} catch (e) {
if (e.type === 'StaleElementReference') {
// element was successfully removed
return true;
}
throw e;
}
});
},
/**
* Enter edit mode by long pressing a given icon, or the first icon on the
* grid if not specified.
*/
enterEditMode: function(icon) {
var actions = this.client.loader.getActions();
var firstIcon = icon ||
this.client.helper.waitForElement(VerticalHome.Selectors.firstIcon);
actions.longPress(firstIcon, 1).perform();
this.client.helper.waitForElement(VerticalHome.Selectors.editHeaderText);
},
/**
* Exits the edit mode by long-pressing the done button and waiting for it
* to disappear.
*/
exitEditMode: function() {
var done = this.client.findElement(VerticalHome.Selectors.editHeaderDone);
done.click();
this.client.helper.waitForElementToDisappear(done);
},
/**
* Emulates pressing of the hardware home button.
*/
pressHomeButton: function() {
this.client.executeScript(function() {
var home = new CustomEvent('home');
window.dispatchEvent(home);
});
},
focusRocketBar: function() {
this.client.helper.waitForElement(VerticalHome.Selectors.search).tap();
this.client.switchToFrame();
},
/**
Tap an app icon in the homescreen
@param {String} manifestURL full manifest url path.
*/
launchApp: function(manifestURL) {
// switch back to the homescreen
this.client.switchToFrame();
this.client.switchToFrame(this.system.getHomescreenIframe());
// tap the app in the homescreen
var newApp = this.getIcon(manifestURL);
newApp.tap();
// go to the system app
this.client.switchToFrame();
},
/**
Tap an app icon and switch to it's application iframe.
@param {String} manifestURL full manifest url path.
*/
launchAndSwitchToApp: function(manifestURL) {
var client = this.client.scope({ searchTimeout: 100 });
var frame;
client.waitFor(function() {
this.launchApp(manifestURL);
// wait for the app to show up
try {
frame = client.findElement(
'iframe[mozapp="' + manifestURL + '"]'
);
} catch (e) {
// try again...
return false;
}
client.switchToFrame(frame);
return true;
}.bind(this));
},
/**
Restart the homescreen then refocus on it.
*/
restart: function() {
this.client.executeScript(function() {
window.close();
});
// initialize our frames again since we killed the iframe
this.client.switchToFrame();
this.client.switchToFrame(this.system.getHomescreenIframe());
},
/**
Find and return every id for all the items on the grid... Each element
can be used with `.getIcon` to find the element for a given id.
@return {Array[String]}
*/
getIconIdentifiers: function() {
return this.client.findElements('[data-identifier]').map(function(el) {
return getIconId(el);
});
},
/**
Fetch an icon element on the homescreen.
@param {String} manifestURL must be a fully qualified manifest url.
@return {Marionette.Element}
*/
getIcon: function(manifestUrl, entryPoint) {
return this.client.helper.waitForElement(
'[data-identifier*="' + manifestUrl +
(entryPoint ? '-' + entryPoint : '') + '"]'
);
},
/**
Fetch the nth icon on the homescreen.
@param {number} the icon number we want
@return {Marionette.Element}
*/
getNthIcon: function(number) {
return this.client.helper.waitForElement(
'.icon:nth-of-type(' + number + ')'
);
},
/**
Get the the current meta=theme-color of the homescreen
@return {String}
*/
getThemeColor: function() {
var meta = this.client.findElement(VerticalHome.Selectors.themeColor);
return meta.getAttribute('content');
},
/**
* Waits for the homescreen to launch and switches to the frame.
*/
waitForLaunch: function() {
this.client.helper.waitForElement('body');
this.client.apps.switchToApp(VerticalHome.URL);
},
/**
* Gets a localized application name from a manifest.
* @param {String} app to open
* @param {String} entryPoint to open
* @param {String} locale
*/
localizedAppName: function(app, entryPoint, locale) {
if (!locale) {
locale = entryPoint;
entryPoint = null;
}
var file = 'app://' + app + '.gaiamobile.org/manifest.webapp';
// use a chrome-scoped Marionette client for the cross-domain XHR
var chromeClient = this.client.scope({context: 'chrome'});
var manifest = chromeClient.executeAsyncScript(function(file) {
var xhr = new XMLHttpRequest();
xhr.open('GET', file, true);
xhr.onload = function(o) {
var data = JSON.parse(xhr.response);
marionetteScriptFinished(data);
};
xhr.send(null);
}, [file]);
var locales;
if (entryPoint) {
locales = manifest.entry_points[entryPoint].locales;
} else {
locales = manifest.locales;
}
if (!locales) {
return false;
}
if (locale.indexOf('-x-ps') > -1) {
return this.client.executeScript(function(locale, name) {
var mozL10n = window.wrappedJSObject.navigator.mozL10n;
return mozL10n.qps[locale].translate(name);
}, [locale, locales['en-US'].name]);
}
return locales[locale].name;
},
/**
* Returns a localized string from a properties file.
* @param {String} key of the string to lookup.
*/
l10n: function(key) {
var string = this.client.executeScript(function(key) {
return window.wrappedJSObject.navigator.mozL10n.get(key);
}, [key]);
return string;
},
containsClass: function(selector, clazz) {
return this.client.executeScript(function(selector, clazz) {
return document.querySelector(selector).classList.contains(clazz);
}, [selector, clazz]);
},
/**
* Helper function to move an icon to a specified index. Currently uses
* executeScript() and manually fiddles with the homescreen grid logic,
* this is because scripted drag/drop does not work too well within the
* vertically scrolling homescreen on b2g desktop.
* @param {Element} icon The grid icon object.
* @param {Integer} index The position to insert the icon into.
*/
moveIconToIndex: function(icon, index) {
this.client.executeScript(function(identifier, newPos) {
var app = window.wrappedJSObject.app;
var icon = app.grid.getIcon(identifier);
app.grid.moveTo(icon.detail.index, newPos);
app.grid.render();
}, [icon.getAttribute('data-identifier'), index]);
// Wait for the icon to animate into place
var actions = this.client.loader.getActions();
actions.wait(1).perform();
},
/**
* Opens the context menu by long-pressing on the first available placeholder.
* If there is no placeholder, the first icon will be dragged to the top of
* the grid to create a new group, and thus new placeholders.
*/
openContextMenu: function() {
var actions = this.client.loader.getActions();
var body = this.client.findElement('body');
actions.longPress(body, 1).perform();
}
};
module.exports = VerticalHome;