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
115
gaia/apps/system/test/marionette/lib/activitycallerapp.js
Executable file
115
gaia/apps/system/test/marionette/lib/activitycallerapp.js
Executable file
|
|
@ -0,0 +1,115 @@
|
|||
'use strict';
|
||||
|
||||
function ActivityCallerApp(client) {
|
||||
this.client = client;
|
||||
}
|
||||
module.exports = ActivityCallerApp;
|
||||
|
||||
ActivityCallerApp.ORIGIN = 'app://activitycaller.gaiamobile.org';
|
||||
|
||||
ActivityCallerApp.Selector = Object.freeze({
|
||||
header: 'h1',
|
||||
launchactivity: '#launchactivity',
|
||||
testchainactivity: '#testchainactivity',
|
||||
testdefaultactivity: '#testdefaultactivity',
|
||||
closeButton: '#close',
|
||||
inputElement: '#input',
|
||||
dateInputElement: '#dateinput',
|
||||
selectElement: '#select'
|
||||
});
|
||||
|
||||
ActivityCallerApp.prototype = {
|
||||
get header() {
|
||||
return this.client.findElement(ActivityCallerApp.Selector.header);
|
||||
},
|
||||
|
||||
get launchactivity() {
|
||||
return this.client.findElement(ActivityCallerApp.Selector.launchactivity);
|
||||
},
|
||||
|
||||
get testchainactivity() {
|
||||
return this.client.findElement(
|
||||
ActivityCallerApp.Selector.testchainactivity);
|
||||
},
|
||||
|
||||
get testdefaultactivity() {
|
||||
return this.client.findElement(
|
||||
ActivityCallerApp.Selector.testdefaultactivity);
|
||||
},
|
||||
|
||||
get closeButton() {
|
||||
return this.client.findElement(ActivityCallerApp.Selector.closeButton);
|
||||
},
|
||||
|
||||
get inputElement() {
|
||||
return this.client.findElement(ActivityCallerApp.Selector.inputElement);
|
||||
},
|
||||
|
||||
get selectElement() {
|
||||
return this.client.findElement(ActivityCallerApp.Selector.selectElement);
|
||||
},
|
||||
|
||||
get dateInputElement() {
|
||||
return this.client.findElement(ActivityCallerApp.Selector.dateInputElement);
|
||||
},
|
||||
|
||||
switchTo: function() {
|
||||
this.client.switchToFrame();
|
||||
this.client.apps.switchToApp(ActivityCallerApp.ORIGIN);
|
||||
},
|
||||
|
||||
launch: function() {
|
||||
var client = this.client;
|
||||
client.apps.launch(ActivityCallerApp.ORIGIN);
|
||||
client.apps.switchToApp(ActivityCallerApp.ORIGIN);
|
||||
client.helper.waitForElement('body');
|
||||
},
|
||||
|
||||
startActivity: function() {
|
||||
this.switchTo();
|
||||
this.launchactivity.tap();
|
||||
this.client.switchToFrame();
|
||||
},
|
||||
|
||||
startChainActivity: function() {
|
||||
this.switchTo();
|
||||
this.testchainactivity.tap();
|
||||
this.client.switchToFrame();
|
||||
},
|
||||
|
||||
startDefaultActivity: function() {
|
||||
this.switchTo();
|
||||
this.testdefaultactivity.tap();
|
||||
this.client.switchToFrame();
|
||||
},
|
||||
|
||||
close: function() {
|
||||
this.switchTo();
|
||||
this.closeButton.tap();
|
||||
this.client.switchToFrame();
|
||||
},
|
||||
|
||||
focusTextInput: function() {
|
||||
this.switchTo();
|
||||
this.inputElement.tap();
|
||||
this.client.switchToFrame();
|
||||
},
|
||||
|
||||
focusSelect: function() {
|
||||
this.switchTo();
|
||||
this.selectElement.tap();
|
||||
this.client.switchToFrame();
|
||||
},
|
||||
|
||||
focusDateInput: function() {
|
||||
this.switchTo();
|
||||
this.dateInputElement.tap();
|
||||
this.client.switchToFrame();
|
||||
},
|
||||
|
||||
blurFocusedInput: function() {
|
||||
this.switchTo();
|
||||
this.header.tap();
|
||||
this.client.switchToFrame();
|
||||
}
|
||||
};
|
||||
28
gaia/apps/system/test/marionette/lib/ambient_indicator.js
Executable file
28
gaia/apps/system/test/marionette/lib/ambient_indicator.js
Executable file
|
|
@ -0,0 +1,28 @@
|
|||
|
||||
'use strict';
|
||||
(function(module) {
|
||||
var AmbientIndicator = function(client) {
|
||||
this.client = client;
|
||||
};
|
||||
|
||||
AmbientIndicator.prototype = {
|
||||
Selectors: {
|
||||
element: '#ambient-indicator'
|
||||
},
|
||||
|
||||
get displayed() {
|
||||
return this.client.findElement(this.Selectors.element).displayed();
|
||||
},
|
||||
|
||||
waitForDisplayed: function() {
|
||||
this.client.helper.waitForElement(this.Selectors.element);
|
||||
},
|
||||
|
||||
waitForHidden: function() {
|
||||
this.client.helper.waitForElementToDisappear(this.Selectors.element);
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
module.exports = AmbientIndicator;
|
||||
})(module);
|
||||
213
gaia/apps/system/test/marionette/lib/app_install.js
Executable file
213
gaia/apps/system/test/marionette/lib/app_install.js
Executable file
|
|
@ -0,0 +1,213 @@
|
|||
'use strict';
|
||||
|
||||
/**
|
||||
* A Marionette test helper for app installation flow.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @constructor
|
||||
* @param {Marionette.Client} client Marionette client to use.
|
||||
*/
|
||||
function AppInstall(client) {
|
||||
this.client = client.scope({ searchTimeout: 200000 });
|
||||
}
|
||||
|
||||
module.exports = AppInstall;
|
||||
|
||||
AppInstall.Selector = Object.freeze({
|
||||
// General app install dialog
|
||||
installDialog: '#app-install-dialog',
|
||||
installButton: '#app-install-install-button',
|
||||
addonWarning: '#app-install-dialog #system-addon-warning',
|
||||
installToast: '#screen > gaia-toast.banner',
|
||||
// Setup dialog after installation
|
||||
setupDialog: '#setup-installed-app-dialog',
|
||||
setupButton: '#setup-confirm-button',
|
||||
// Ime Layout selection menu
|
||||
imeDialog: '#ime-layout-dialog',
|
||||
imeLayoutOption: '#ime-list li',
|
||||
imeConfirmButton: '#ime-confirm-button'
|
||||
});
|
||||
|
||||
AppInstall.prototype = {
|
||||
|
||||
get installDialog() {
|
||||
return this.client.findElement(AppInstall.Selector.installDialog);
|
||||
},
|
||||
|
||||
get installButton() {
|
||||
return this.client.findElement(AppInstall.Selector.installButton);
|
||||
},
|
||||
|
||||
get addonWarning() {
|
||||
return this.client.findElement(AppInstall.Selector.addonWarning);
|
||||
},
|
||||
|
||||
get setupDialog() {
|
||||
return this.client.findElement(AppInstall.Selector.setupDialog);
|
||||
},
|
||||
|
||||
get setupButton() {
|
||||
return this.client.findElement(AppInstall.Selector.setupButton);
|
||||
},
|
||||
|
||||
get imeDialog() {
|
||||
return this.client.findElement(AppInstall.Selector.imeDialog);
|
||||
},
|
||||
|
||||
get imeLayoutOption() {
|
||||
return this.client.findElement(AppInstall.Selector.imeLayoutOption);
|
||||
},
|
||||
|
||||
get imeConfirmButton() {
|
||||
return this.client.findElement(AppInstall.Selector.imeConfirmButton);
|
||||
},
|
||||
|
||||
_install: function(type, manifestURL, options) {
|
||||
this.client.executeScript(function install(type, url) {
|
||||
window.wrappedJSObject.navigator.mozApps[type](url);
|
||||
}, [type, manifestURL]);
|
||||
|
||||
options = options || { allowInstall: true };
|
||||
if (options.allowInstall) {
|
||||
this.waitForDialog(this.installDialog);
|
||||
// Wait for the install button visible both to user and the screen reader.
|
||||
this.client.helper.waitForElement(this.installButton);
|
||||
// Click install button on the app install dialog
|
||||
this.installButton.click();
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Install an app.
|
||||
* @param {string} manifestURL The manifestURL of the app to be installed.
|
||||
* @param {Object} [options] The options for installation.
|
||||
* @param {boolean} [options.allowInstall=true] If true, allow the
|
||||
* installation directly.
|
||||
*/
|
||||
installPackage: function installPackage(manifestURL, options) {
|
||||
return this._install('installPackage', manifestURL, options);
|
||||
},
|
||||
|
||||
/**
|
||||
* Install an app.
|
||||
* @param {string} manifestURL The manifestURL of the app to be installed.
|
||||
* @param {Object} [options] The options for installation.
|
||||
* @param {boolean} [options.allowInstall=true] If true, allow the
|
||||
* installation directly.
|
||||
*/
|
||||
install: function install(manifestURL, options) {
|
||||
return this._install('install', manifestURL, options);
|
||||
},
|
||||
|
||||
/**
|
||||
* Uninstall an app.
|
||||
* @param {string} [manifestURL] The manifestURL of the app.
|
||||
*/
|
||||
uninstall: function(manifestURL) {
|
||||
this.client.switchToFrame();
|
||||
|
||||
this.client.executeAsyncScript(function uninstall(url) {
|
||||
var win = window.wrappedJSObject;
|
||||
var mgmt = win.navigator.mozApps.mgmt;
|
||||
|
||||
mgmt.getAll().onsuccess = function(e) {
|
||||
var app = e.target.result.find(function(app) {
|
||||
return app.manifestURL === url;
|
||||
});
|
||||
|
||||
mgmt.uninstall(app);
|
||||
marionetteScriptFinished();
|
||||
};
|
||||
}, [manifestURL]);
|
||||
},
|
||||
|
||||
/**
|
||||
Checks for downloads without applying them.
|
||||
*/
|
||||
stageUpdate: function(manifestURL) {
|
||||
// do it in chrome so we don't need to switch contexts...
|
||||
var client = this.client.scope({ context: 'chrome' });
|
||||
return client.executeAsyncScript(function(manifestURL) {
|
||||
navigator.mozApps.mgmt.getAll().onsuccess = function(e) {
|
||||
var list = e.target.result;
|
||||
var app = list.find(function(app) {
|
||||
return app.manifestURL === manifestURL;
|
||||
});
|
||||
var req = app.checkForUpdate();
|
||||
req.onsuccess = req.onerror = function() {
|
||||
marionetteScriptFinished();
|
||||
};
|
||||
};
|
||||
}, [manifestURL]);
|
||||
},
|
||||
|
||||
/**
|
||||
Updates an installed app by it's manifest url.
|
||||
|
||||
@param {String} manifestURL for the application.
|
||||
*/
|
||||
update: function(manifestURL) {
|
||||
// do it in chrome so we don't need to switch contexts...
|
||||
var client = this.client.scope({ context: 'chrome' });
|
||||
return client.executeAsyncScript(function(manifestURL) {
|
||||
navigator.mozApps.mgmt.getAll().onsuccess = function(e) {
|
||||
var list = e.target.result;
|
||||
var app = list.find(function(app) {
|
||||
return app.manifestURL === manifestURL;
|
||||
});
|
||||
|
||||
var req = app.checkForUpdate();
|
||||
req.onsuccess = function() {
|
||||
app.download();
|
||||
marionetteScriptFinished();
|
||||
};
|
||||
|
||||
req.onerror = function() {
|
||||
marionetteScriptFinished();
|
||||
};
|
||||
};
|
||||
}, [manifestURL]);
|
||||
},
|
||||
|
||||
/**
|
||||
* Dismiss the app install toast banner.
|
||||
*/
|
||||
dismissToast: function() {
|
||||
var toast = this.waitForToast();
|
||||
toast.tap();
|
||||
this.client.helper.waitForElementToDisappear(toast);
|
||||
},
|
||||
|
||||
/**
|
||||
* Wait for the app install toast banner.
|
||||
*/
|
||||
waitForToast: function() {
|
||||
return this.client.helper.waitForElement(AppInstall.Selector.installToast);
|
||||
},
|
||||
|
||||
/**
|
||||
* Wait for the specified dialog to show up.
|
||||
* @param {Marionette.Element} element The dialog element to wait for.
|
||||
*/
|
||||
waitForDialog: function waitForAppInstallDialog(element) {
|
||||
return this.client.helper.waitForElement(element);
|
||||
},
|
||||
|
||||
isAppInstalled: function(app) {
|
||||
if (typeof app !== 'string') {
|
||||
app = app.manifestURL;
|
||||
}
|
||||
var isInstalled = this.client.executeAsyncScript(function(url) {
|
||||
var win = window.wrappedJSObject;
|
||||
var mgmt = win.navigator.mozApps.mgmt;
|
||||
mgmt.getAll().onsuccess = function(e) {
|
||||
var matchedApp = e.target.result.find(function(app) {
|
||||
return app.manifestURL === url;
|
||||
});
|
||||
marionetteScriptFinished(!!matchedApp);
|
||||
};
|
||||
}, [app]);
|
||||
return isInstalled;
|
||||
}
|
||||
};
|
||||
99
gaia/apps/system/test/marionette/lib/app_usage_metrics.js
Executable file
99
gaia/apps/system/test/marionette/lib/app_usage_metrics.js
Executable file
|
|
@ -0,0 +1,99 @@
|
|||
'use strict';
|
||||
|
||||
function AppUsageMetrics(client) {
|
||||
this.client = client;
|
||||
this.data = null;
|
||||
}
|
||||
|
||||
module.exports = AppUsageMetrics;
|
||||
|
||||
AppUsageMetrics.prototype = {
|
||||
client: null,
|
||||
data: null,
|
||||
|
||||
/**
|
||||
* Get the number of times an app was opened then closed
|
||||
* @param {String} The app's base URL (without entry point)
|
||||
* @return {Integer}
|
||||
*/
|
||||
getAppInvocations: function(app) {
|
||||
return this._getAppUsage(app, 'invocations', 0);
|
||||
},
|
||||
|
||||
/**
|
||||
* Get the length of time an app was open for after closed
|
||||
* @param {String} The app's base URL (without entry point)
|
||||
* @return {Integer}
|
||||
*/
|
||||
getAppUsageTime: function(app) {
|
||||
return this._getAppUsage(app, 'usageTime', 0);
|
||||
},
|
||||
|
||||
/**
|
||||
* Get the number of times an app was installed
|
||||
* @param {String} The app's base URL (without entry point)
|
||||
* @return {Integer}
|
||||
*/
|
||||
getAppInstalls: function(app) {
|
||||
return this._getAppUsage(app, 'installs', 0);
|
||||
},
|
||||
|
||||
/**
|
||||
* Get the record of an app's uninstall, if it exists, or undefined.
|
||||
* @param {String} The app's base URL (without entry point)
|
||||
* @return {Integer}
|
||||
*/
|
||||
getAppUninstalls: function(app) {
|
||||
return this._getAppUsage(app, 'uninstalls', 0);
|
||||
},
|
||||
|
||||
/**
|
||||
* Refresh metrics data from the system app
|
||||
*/
|
||||
_getAppUsage: function(app, key, defaultValue) {
|
||||
var currentFrame = this.client.frame;
|
||||
var client = this.client;
|
||||
var data = null;
|
||||
|
||||
client.switchToFrame();
|
||||
client.waitFor(() => {
|
||||
data = client.executeScript(function(app) {
|
||||
var aum = window.wrappedJSObject.core.appUsageMetrics;
|
||||
var metrics = aum && aum.metrics;
|
||||
return metrics && metrics.getAppUsage(app);
|
||||
}, [app]);
|
||||
return !!data;
|
||||
});
|
||||
if (currentFrame) {
|
||||
client.switchToFrame(currentFrame);
|
||||
}
|
||||
return data ? (data[key] || defaultValue) : defaultValue;
|
||||
},
|
||||
|
||||
waitForAUMInstance: function() {
|
||||
this.client.switchToFrame();
|
||||
this.client.waitFor(() => {
|
||||
return this.client.executeScript(function() {
|
||||
return window.wrappedJSObject.core.appUsageMetrics;
|
||||
});
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* Start the AppUsageMetrics service and wait for it to finish
|
||||
*/
|
||||
waitForStartup: function() {
|
||||
this.waitForAUMInstance();
|
||||
this.client.executeAsyncScript(function() {
|
||||
var AUM = window.wrappedJSObject.core.appUsageMetrics;
|
||||
if (AUM.collecting) {
|
||||
marionetteScriptFinished();
|
||||
return;
|
||||
}
|
||||
|
||||
AUM.startCollecting(function() {
|
||||
marionetteScriptFinished();
|
||||
});
|
||||
});
|
||||
}
|
||||
};
|
||||
63
gaia/apps/system/test/marionette/lib/audio_channel_helper.js
Executable file
63
gaia/apps/system/test/marionette/lib/audio_channel_helper.js
Executable file
|
|
@ -0,0 +1,63 @@
|
|||
'use strict';
|
||||
|
||||
function AudioChannelHelper(client) {
|
||||
this.client = client;
|
||||
}
|
||||
|
||||
module.exports = AudioChannelHelper;
|
||||
|
||||
AudioChannelHelper.prototype = {
|
||||
/**
|
||||
* Check the audio channel is playing or not.
|
||||
*
|
||||
* @param {String} url The App URL.
|
||||
* @param {String} audioChannel An audio channel type.
|
||||
* @param {Boolean} isPlaying Expect audio channel is playing or not.
|
||||
*/
|
||||
isPlaying: function(url, audioChannel, isPlaying) {
|
||||
var client = this.client;
|
||||
client.switchToFrame();
|
||||
client.waitFor(function() {
|
||||
return isPlaying === client.executeScript(function(url, audioChannel) {
|
||||
return window.wrappedJSObject.core.appCore.appWindowManager
|
||||
.getApp(url).audioChannels.get(audioChannel).isPlaying();
|
||||
}, [url, audioChannel]);
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* Check the audio channel is fading out or not.
|
||||
*
|
||||
* @param {String} url The App URL.
|
||||
* @param {String} audioChannel An audio channel type.
|
||||
* @param {Boolean} isFadingOut Expect audio channel is fading out or not.
|
||||
*/
|
||||
isFadingOut: function(url, audioChannel, isFadingOut) {
|
||||
var client = this.client;
|
||||
client.switchToFrame();
|
||||
client.waitFor(function() {
|
||||
return isFadingOut === client.executeScript(function(url, audioChannel) {
|
||||
return window.wrappedJSObject.core.appCore.appWindowManager
|
||||
.getApp(url).audioChannels.get(audioChannel).isFadingOut();
|
||||
}, [url, audioChannel]);
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* Check the audio channel is vibrating out or not.
|
||||
*
|
||||
* @param {String} url The App URL.
|
||||
* @param {String} audioChannel An audio channel type.
|
||||
* @param {Boolean} isVibrating Expect audio channel is vibrating or not.
|
||||
*/
|
||||
isVibrating: function(url, audioChannel, isVibrating) {
|
||||
var client = this.client;
|
||||
client.switchToFrame();
|
||||
client.waitFor(function() {
|
||||
return isVibrating === client.executeScript(function(url, audioChannel) {
|
||||
return window.wrappedJSObject.core.appCore.appWindowManager
|
||||
.getApp(url).audioChannels.get(audioChannel).isVibrating();
|
||||
}, [url, audioChannel]);
|
||||
});
|
||||
}
|
||||
};
|
||||
99
gaia/apps/system/test/marionette/lib/audio_channel_test_app.js
Executable file
99
gaia/apps/system/test/marionette/lib/audio_channel_test_app.js
Executable file
|
|
@ -0,0 +1,99 @@
|
|||
'use strict';
|
||||
|
||||
function AudioChannelTestApp(client, origin) {
|
||||
this.client = client;
|
||||
this.origin = origin;
|
||||
}
|
||||
|
||||
module.exports = AudioChannelTestApp;
|
||||
|
||||
AudioChannelTestApp.Selector = Object.freeze({
|
||||
normalPlay: '#normal .play',
|
||||
normalPause: '#normal .pause',
|
||||
contentPlay: '#content .play',
|
||||
contentPause: '#content .pause',
|
||||
alarmPlay: '#alarm .play',
|
||||
alarmPause: '#alarm .pause',
|
||||
systemPlay: '#system .play',
|
||||
systemPause: '#system .pause',
|
||||
ringerPlay: '#ringer .play',
|
||||
ringerPause: '#ringer .pause',
|
||||
telephonyPlay: '#telephony .play',
|
||||
telephonyPause: '#telephony .pause',
|
||||
notificationPlay: '#notification .play',
|
||||
notificationPause: '#notification .pause',
|
||||
publicnotificationPlay: '#publicnotification .play',
|
||||
publicnotificationPause: '#publicnotification .pause',
|
||||
});
|
||||
|
||||
AudioChannelTestApp.prototype = {
|
||||
client: null,
|
||||
|
||||
get normalPlay() {
|
||||
return this.client.findElement(AudioChannelTestApp.Selector.normalPlay);
|
||||
},
|
||||
|
||||
get normalPause() {
|
||||
return this.client.findElement(AudioChannelTestApp.Selector.normalPause);
|
||||
},
|
||||
|
||||
get contentPlay() {
|
||||
return this.client.findElement(AudioChannelTestApp.Selector.contentPlay);
|
||||
},
|
||||
|
||||
get contentPause() {
|
||||
return this.client.findElement(AudioChannelTestApp.Selector.contentPause);
|
||||
},
|
||||
|
||||
get alarmPlay() {
|
||||
return this.client.findElement(AudioChannelTestApp.Selector.alarmPlay);
|
||||
},
|
||||
|
||||
get alarmPause() {
|
||||
return this.client.findElement(AudioChannelTestApp.Selector.alarmPause);
|
||||
},
|
||||
|
||||
get systemPlay() {
|
||||
return this.client.findElement(AudioChannelTestApp.Selector.systemPlay);
|
||||
},
|
||||
|
||||
get systemPause() {
|
||||
return this.client.findElement(AudioChannelTestApp.Selector.systemPause);
|
||||
},
|
||||
|
||||
get ringerPlay() {
|
||||
return this.client.findElement(AudioChannelTestApp.Selector.ringerPlay);
|
||||
},
|
||||
|
||||
get ringerPause() {
|
||||
return this.client.findElement(AudioChannelTestApp.Selector.ringerPause);
|
||||
},
|
||||
|
||||
get telephonyPlay() {
|
||||
return this.client.findElement(AudioChannelTestApp.Selector.telephonyPlay);
|
||||
},
|
||||
|
||||
get telephonyPause() {
|
||||
return this.client.findElement(AudioChannelTestApp.Selector.telephonyPause);
|
||||
},
|
||||
|
||||
get notificationPlay() {
|
||||
return this.client
|
||||
.findElement(AudioChannelTestApp.Selector.notificationPlay);
|
||||
},
|
||||
|
||||
get notificationPause() {
|
||||
return this.client
|
||||
.findElement(AudioChannelTestApp.Selector.notificationPause);
|
||||
},
|
||||
|
||||
get publicnotificationPlay() {
|
||||
return this.client
|
||||
.findElement(AudioChannelTestApp.Selector.publicnotificationPlay);
|
||||
},
|
||||
|
||||
get publicnotificationPause() {
|
||||
return this.client
|
||||
.findElement(AudioChannelTestApp.Selector.publicnotificationPause);
|
||||
},
|
||||
};
|
||||
166
gaia/apps/system/test/marionette/lib/bookmark.js
Executable file
166
gaia/apps/system/test/marionette/lib/bookmark.js
Executable file
|
|
@ -0,0 +1,166 @@
|
|||
'use strict';
|
||||
/* global module */
|
||||
|
||||
var Rocketbar = require('./rocketbar');
|
||||
|
||||
function Bookmark(client) {
|
||||
this.client = client;
|
||||
this.rocketbar = new Rocketbar(client);
|
||||
this.search = client.loader.getAppClass('search');
|
||||
this.system = client.loader.getAppClass('system');
|
||||
}
|
||||
|
||||
Bookmark.URL = 'app://bookmark.gaiamobile.org';
|
||||
|
||||
Bookmark.prototype = {
|
||||
|
||||
URL: Bookmark.URL,
|
||||
Selectors: {
|
||||
'bookmarkAddButton': '#done-button',
|
||||
'bookmarkTitle': '#bookmark-title',
|
||||
'mozbrowser': '.inline-activity.active iframe[mozbrowser]',
|
||||
'appInstallationSection': '#app-installation',
|
||||
'installAppButton': '#install-app-button',
|
||||
'appName': '#app-name',
|
||||
'appIcon': '#app-icon',
|
||||
'removeButton': '#remove-action'
|
||||
},
|
||||
|
||||
get addButton() {
|
||||
return this.client.helper.waitForElement(
|
||||
this.Selectors.bookmarkAddButton);
|
||||
},
|
||||
|
||||
get currentTabFrame() {
|
||||
return this.client.helper.waitForElement(
|
||||
this.Selectors.mozbrowser);
|
||||
},
|
||||
|
||||
get bookmarkTitle() {
|
||||
return this.client.helper.waitForElement(
|
||||
this.Selectors.bookmarkTitle);
|
||||
},
|
||||
|
||||
get installAppButton() {
|
||||
return this.client.helper.waitForElement(
|
||||
this.Selectors.installAppButton);
|
||||
},
|
||||
|
||||
get appName() {
|
||||
return this.client.helper.waitForElement(
|
||||
this.Selectors.appName);
|
||||
},
|
||||
|
||||
get appIcon() {
|
||||
return this.client.helper.waitForElement(
|
||||
this.Selectors.appIcon);
|
||||
},
|
||||
|
||||
get removeButton() {
|
||||
return this.client.helper.waitForElement(
|
||||
this.Selectors.removeButton);
|
||||
},
|
||||
|
||||
/**
|
||||
* Switches to the bookmark activity and adds the current bookmark.
|
||||
* Switches back to the system app upon completion.
|
||||
*/
|
||||
add: function() {
|
||||
this.client.switchToFrame(this.currentTabFrame);
|
||||
|
||||
var selectors = this.Selectors;
|
||||
var client = this.client.scope({ searchTimeout: 100 });
|
||||
client.waitFor(function() {
|
||||
try {
|
||||
var text = client.findElement(selectors.bookmarkTitle).scriptWith(
|
||||
function(el) { return el.value; });
|
||||
return text.length > 0;
|
||||
} catch(e) {
|
||||
return false;
|
||||
}
|
||||
}.bind(this));
|
||||
this.addButton.click();
|
||||
|
||||
this.client.switchToFrame();
|
||||
this.client.helper.waitForElementToDisappear(this.currentTabFrame);
|
||||
},
|
||||
|
||||
/**
|
||||
* Navigates to a given url from the homescreen
|
||||
* and saves it as a bookmark.
|
||||
*/
|
||||
openAndSave: function(url) {
|
||||
this.rocketbar.homescreenFocus();
|
||||
this.rocketbar.enterText(url, true);
|
||||
|
||||
this.client.waitFor(function() {
|
||||
this.client.switchToFrame();
|
||||
try {
|
||||
this.client.findElement(
|
||||
this.system.Selector.appChromeContextLink).tap();
|
||||
} catch(e) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}.bind(this));
|
||||
this.system.appChromeContextMenuBookmark.click();
|
||||
|
||||
this.add();
|
||||
},
|
||||
|
||||
/**
|
||||
* Navigates to a given url from the homescreen
|
||||
* and currently just looks for install section with correct app name.
|
||||
*/
|
||||
openAndInstall: function(url, name, iconURL) {
|
||||
this.rocketbar.homescreenFocus();
|
||||
this.rocketbar.enterText(url, true);
|
||||
|
||||
this.client.waitFor(function() {
|
||||
this.client.switchToFrame();
|
||||
try {
|
||||
this.client.findElement(
|
||||
this.system.Selector.appChromeContextLink).tap();
|
||||
} catch(e) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}.bind(this));
|
||||
this.system.appChromeContextMenuBookmark.click();
|
||||
|
||||
this.client.switchToFrame(this.currentTabFrame);
|
||||
this.client.waitFor((function() {
|
||||
console.log('BTF ' + this.appIcon.getAttribute('src'));
|
||||
return this.appName.text() == name &&
|
||||
this.appIcon.displayed &&
|
||||
this.appIcon.getAttribute('src').indexOf(iconURL) != -1;
|
||||
}).bind(this));
|
||||
},
|
||||
|
||||
/**
|
||||
* Renames a bookmark and emulates pressing the 'enter' key after doing so.
|
||||
*/
|
||||
renameAndPressEnter: function(newName) {
|
||||
this.client.switchToFrame(this.currentTabFrame);
|
||||
this.bookmarkTitle.clear();
|
||||
this.bookmarkTitle.sendKeys(newName);
|
||||
this.addButton.click();
|
||||
|
||||
this.client.switchToFrame();
|
||||
this.client.helper.waitForElementToDisappear(this.currentTabFrame);
|
||||
},
|
||||
|
||||
/**
|
||||
* Perform action on a pin confirm dialog.
|
||||
*
|
||||
* @param {String} action The action to perform on the pin confirm dialog.
|
||||
*/
|
||||
pinConfirmDialog: function(action) {
|
||||
var selector = 'form[data-type="confirm"]';
|
||||
var dialog = this.client.helper.waitForElement(selector);
|
||||
var actionButton = dialog.findElement('#' + action + '-action');
|
||||
actionButton.tap();
|
||||
}
|
||||
};
|
||||
|
||||
module.exports = Bookmark;
|
||||
39
gaia/apps/system/test/marionette/lib/cell_broadcast_system.js
Executable file
39
gaia/apps/system/test/marionette/lib/cell_broadcast_system.js
Executable file
|
|
@ -0,0 +1,39 @@
|
|||
|
||||
'use strict';
|
||||
(function(module) {
|
||||
|
||||
var CellBroadcastSystem = function(client) {
|
||||
this.client = client;
|
||||
};
|
||||
|
||||
CellBroadcastSystem.prototype = {
|
||||
Selectors: {
|
||||
element: '#modal-dialog-alert'
|
||||
},
|
||||
|
||||
get dialog() {
|
||||
return this.client.findElement(this.Selectors.element);
|
||||
},
|
||||
|
||||
get visible() {
|
||||
return this.client.findElement(this.Selectors.element).displayed();
|
||||
},
|
||||
|
||||
show: function(event) {
|
||||
this._mockMobile();
|
||||
this.client.executeScript(function(event) {
|
||||
window.wrappedJSObject.Service.request(
|
||||
'CellBroadcastSystem:show', event);
|
||||
}, [event]);
|
||||
},
|
||||
|
||||
_mockMobile: function() {
|
||||
this.client.executeScript(function() {
|
||||
window.wrappedJSObject.navigator.mozMobileConnections = [];
|
||||
});
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
module.exports = CellBroadcastSystem;
|
||||
})(module);
|
||||
43
gaia/apps/system/test/marionette/lib/confirm_dialog.js
Executable file
43
gaia/apps/system/test/marionette/lib/confirm_dialog.js
Executable file
|
|
@ -0,0 +1,43 @@
|
|||
'use strict';
|
||||
|
||||
/**
|
||||
* A helper module to interact with the confirm dialog in system app.
|
||||
*/
|
||||
|
||||
function ConfirmDialog(client) {
|
||||
this.client = client.scope({ searchTimeout: 20000 });
|
||||
}
|
||||
|
||||
module.exports = ConfirmDialog;
|
||||
|
||||
ConfirmDialog.prototype = {
|
||||
/**
|
||||
Fetch a particular type of a gaia-confirm dialog.
|
||||
|
||||
@param {String} type of dialog.
|
||||
*/
|
||||
getElement: function(type) {
|
||||
var selector = this.getDialogSelector(type);
|
||||
return this.client.helper.waitForElement(selector);
|
||||
},
|
||||
|
||||
getDialogSelector: function(type) {
|
||||
return 'gaia-confirm[data-type="' + type + '"]';
|
||||
},
|
||||
|
||||
/**
|
||||
Click confirm on a particular type of confirmation dialog.
|
||||
|
||||
@param {String} type of dialog.
|
||||
@param {String} selector of the button. Defaults to .confirm.
|
||||
*/
|
||||
confirm: function(type, button) {
|
||||
var dialogSelector = this.getDialogSelector(type);
|
||||
var confirmSelector = dialogSelector + ' .confirm';
|
||||
var confirmButton = this.client.helper.waitForElement(confirmSelector);
|
||||
this.client.waitFor(function() {
|
||||
return confirmButton.displayed();
|
||||
});
|
||||
confirmButton.click();
|
||||
}
|
||||
};
|
||||
68
gaia/apps/system/test/marionette/lib/dialog.js
Executable file
68
gaia/apps/system/test/marionette/lib/dialog.js
Executable file
|
|
@ -0,0 +1,68 @@
|
|||
'use strict';
|
||||
|
||||
function Dialog(client) {
|
||||
this.client = client.scope({
|
||||
searchTimeout: 20000
|
||||
});
|
||||
}
|
||||
|
||||
module.exports = Dialog;
|
||||
|
||||
Dialog.Selector = {
|
||||
visibleDialog: '.generic-dialog.visible',
|
||||
okButton: '.generic-dialog.visible .affirmative',
|
||||
messageContainer: '.generic-dialog.visible .inner',
|
||||
promptInput: '.generic-dialog.visible .modal-dialog-prompt-input',
|
||||
menu: '.generic-dialog.visible menu',
|
||||
title: '.generic-dialog.visible .inner h3'
|
||||
};
|
||||
|
||||
Dialog.prototype = {
|
||||
client: null,
|
||||
system: null,
|
||||
alert: function(message) {
|
||||
this.client.executeScript(function(message) {
|
||||
return window.setTimeout(function() {
|
||||
window.wrappedJSObject.alert(message);
|
||||
});
|
||||
}, [message]);
|
||||
},
|
||||
confirm: function(message) {
|
||||
this.client.executeScript(function(message) {
|
||||
return window.setTimeout(function() {
|
||||
window.wrappedJSObject.confirm(message);
|
||||
});
|
||||
}, [message]);
|
||||
},
|
||||
prompt: function(message) {
|
||||
this.client.executeScript(function(message) {
|
||||
return window.setTimeout(function() {
|
||||
window.wrappedJSObject.prompt(message);
|
||||
});
|
||||
}, [message]);
|
||||
},
|
||||
|
||||
get current() {
|
||||
return this.client.helper.waitForElement(Dialog.Selector.visibleDialog);
|
||||
},
|
||||
|
||||
get okButton() {
|
||||
return this.client.helper.waitForElement(Dialog.Selector.okButton);
|
||||
},
|
||||
|
||||
get menu() {
|
||||
return this.client.findElement(Dialog.Selector.menu);
|
||||
},
|
||||
|
||||
get title() {
|
||||
return this.client.helper.waitForElement(Dialog.Selector.title);
|
||||
},
|
||||
|
||||
get messageContainer() {
|
||||
return this.client.findElement(Dialog.Selector.messageContainer);
|
||||
},
|
||||
|
||||
get promptInput() {
|
||||
return this.client.findElement(Dialog.Selector.promptInput);
|
||||
}
|
||||
};
|
||||
451
gaia/apps/system/test/marionette/lib/ensure.js
Executable file
451
gaia/apps/system/test/marionette/lib/ensure.js
Executable file
|
|
@ -0,0 +1,451 @@
|
|||
'use strict';
|
||||
(function(module) {
|
||||
var Ensure = function() {};
|
||||
Ensure.prototype.start =
|
||||
function(client) {
|
||||
this._actions = client.loader.getActions();
|
||||
this.client = client;
|
||||
this.elements = {};
|
||||
this.footprintCounter = 0;
|
||||
this.configs = {
|
||||
footprint: 'marionette-js-ensure-footprint',
|
||||
selectorFootprint: '[data-marionette-js-ensure-footprint]'
|
||||
};
|
||||
// XXX: when it becomes an app, should has its own app info class.
|
||||
this.lockScreenFrameOrigin = 'app://lockscreen.gaiamobile.org';
|
||||
this.lockScreenFrameId = 'lockscreen-frame';
|
||||
return this;
|
||||
};
|
||||
|
||||
Ensure.prototype.systemReady =
|
||||
function() {
|
||||
this.client.waitFor((function() {
|
||||
return this.client.executeScript(function() {
|
||||
return null ===
|
||||
window.wrappedJSObject.document.getElementById('os-logo');
|
||||
});
|
||||
}).bind(this));
|
||||
};
|
||||
|
||||
/**
|
||||
* Check if the element is valid (not stale or dettached).
|
||||
*/
|
||||
Ensure.prototype.validateElement =
|
||||
function(element) {
|
||||
try {
|
||||
element.scriptWith(function() {});
|
||||
} catch (e) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
};
|
||||
|
||||
Ensure.prototype.launch =
|
||||
function(origin) {
|
||||
this.client.apps.launch(origin);
|
||||
this.client.apps.switchToApp(origin);
|
||||
|
||||
// Wait until the app has told us it's fully loaded.
|
||||
this.client.waitFor((function() {
|
||||
return null !== this.client.findElement('body.loaded');
|
||||
}).bind(this));
|
||||
this.frame(origin);
|
||||
return this;
|
||||
};
|
||||
|
||||
/**
|
||||
* Since navigator.mozSettings sometimes is null,
|
||||
* especially when switching into the app frame without
|
||||
* the permission, we need to do this check.
|
||||
*/
|
||||
Ensure.prototype.settings =
|
||||
function() {
|
||||
this.client.waitFor((function() {
|
||||
var result = this.client.executeScript(function() {
|
||||
var settings = window.wrappedJSObject.navigator.mozSettings;
|
||||
var isNotNull = (null !== settings);
|
||||
settings = null;
|
||||
return isNotNull;
|
||||
});
|
||||
return result;
|
||||
}).bind(this));
|
||||
};
|
||||
|
||||
Ensure.prototype.close =
|
||||
function(origin) {
|
||||
this.client.switchToFrame();
|
||||
var appFrame = this.client.findElement('iframe[src*="' + origin + '"]');
|
||||
this.client.switchToFrame(appFrame);
|
||||
this.client.executeScript(function() {
|
||||
window.wrappedJSObject.close();
|
||||
});
|
||||
this.client.waitFor((function() {
|
||||
var searchTimeout = this.client.searchTimeout;
|
||||
this.client.setSearchTimeout(0);
|
||||
|
||||
var frames = this.client.findElements('iframe[mozapp]');
|
||||
this.client.setSearchTimeout(searchTimeout);
|
||||
|
||||
return frames.reduce(function(prev, frame) {
|
||||
if (frame.getAttribute('mozapp').match(origin)) {
|
||||
return false;
|
||||
}
|
||||
}, true);
|
||||
}).bind(this));
|
||||
return this;
|
||||
};
|
||||
|
||||
Ensure.prototype.frame =
|
||||
function(origin) {
|
||||
// System is the only one exception to check visible:
|
||||
// everything is withing System.
|
||||
if (!origin) {
|
||||
this.client.switchToFrame();
|
||||
return this;
|
||||
} else if (origin === this.lockScreenFrameOrigin) {
|
||||
// To System frame first.
|
||||
this.client.switchToFrame();
|
||||
|
||||
this.client.executeScript(function(origin, lockScreenFrameId) {
|
||||
// XXX: Before we make LockScreen as an iframe or app,
|
||||
// we need this to do the check, while keep compability.
|
||||
var elementAtCenter = document.elementFromPoint(
|
||||
window.innerWidth>>1 , window.innerHeight>>1);
|
||||
if (lockScreenFrameId === elementAtCenter.id) {
|
||||
return true;
|
||||
}
|
||||
while (null !== elementAtCenter.parentElement) {
|
||||
if (lockScreenFrameId === elementAtCenter.id) {
|
||||
return true;
|
||||
} else {
|
||||
elementAtCenter = elementAtCenter.parentElement;
|
||||
}
|
||||
}
|
||||
throw new Error('Want to switch to LockScreen frame but it is' +
|
||||
'invisible. The last detected element\'s ID is: ' +
|
||||
elementAtCenter.id);
|
||||
}, [origin, this.lockScreenFrameId]);
|
||||
|
||||
return this;
|
||||
}
|
||||
// To System frame first.
|
||||
this.client.switchToFrame();
|
||||
|
||||
this.client.waitFor((function() {
|
||||
return this.client.executeScript(function(origin) {
|
||||
var element = document.elementFromPoint(
|
||||
window.innerWidth>>1 , window.innerHeight>>1);
|
||||
if ('IFRAME' !== element.nodeName) {
|
||||
return false;
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
});
|
||||
}).bind(this));
|
||||
// Check the element at the center of the System frame
|
||||
var frameElement = this.client.executeScript(function(origin) {
|
||||
var element = document.elementFromPoint(
|
||||
window.innerWidth>>1 , window.innerHeight>>1);
|
||||
if (!element.getAttribute('mozapp') &&
|
||||
element.dataset.frameOrigin &&
|
||||
element.dataset.frameOrigin.match(origin)) {
|
||||
// No mozapp frame: ordinary frame.
|
||||
return element;
|
||||
} else if (!element.getAttribute('mozapp').match(origin)) {
|
||||
throw new Error('User would never switch to a invisible frame.' +
|
||||
'the target origin is :' + origin +
|
||||
'the fetched origin is :' + element.getAttribute('mozapp'));
|
||||
}
|
||||
return element;
|
||||
}, [origin]);
|
||||
this.client.switchToFrame(frameElement);
|
||||
this.client.executeScript(function() {
|
||||
if (document.hidden) {
|
||||
throw new Error('User would never switch to a invisible frame. ' +
|
||||
'The frame is hidden.');
|
||||
}
|
||||
});
|
||||
return this;
|
||||
};
|
||||
|
||||
/**
|
||||
* If element is a function, would execute it in client's context and
|
||||
* get the element by returning.
|
||||
*/
|
||||
Ensure.prototype.element =
|
||||
function(element, argumentsList) {
|
||||
if (!element) {
|
||||
throw new Error('No such element or element generator');
|
||||
}
|
||||
if ('function' === typeof element) {
|
||||
element = this.client.executeScript(element, argumentsList);
|
||||
}
|
||||
if (!element) {
|
||||
throw new Error('No such element after execute the generator');
|
||||
}
|
||||
this.elements.current = this.patchElement(element);
|
||||
return this;
|
||||
};
|
||||
|
||||
/**
|
||||
* Set current element with an alias.
|
||||
* So user can manipulate on a new element.
|
||||
*/
|
||||
Ensure.prototype.as =
|
||||
function(name) {
|
||||
this.elements[name] = this.elements.current;
|
||||
return this;
|
||||
};
|
||||
|
||||
/**
|
||||
* Use the specify element as the current element.
|
||||
*/
|
||||
Ensure.prototype.use =
|
||||
function(name) {
|
||||
if (!this.elements[name]) {
|
||||
throw new Error('No such element with the name: ' + name);
|
||||
}
|
||||
this.elements.current = this.elements[name];
|
||||
return this;
|
||||
};
|
||||
|
||||
/**
|
||||
* If give an action, execute it and wait the element become
|
||||
* displayed. If give no such action, check the element directly.
|
||||
*/
|
||||
Ensure.prototype.displayed =
|
||||
function(action) {
|
||||
if ('function' === typeof action) {
|
||||
action();
|
||||
this.client.waitFor((function() {
|
||||
return this.elements.current.displayed();
|
||||
}).bind(this), function() {
|
||||
throw new Error('The element is not displayed even after the action');
|
||||
});
|
||||
} else {
|
||||
if (!this.elements.current.displayed()) {
|
||||
throw new Error('The element is not displayed');
|
||||
}
|
||||
}
|
||||
return this;
|
||||
};
|
||||
|
||||
Ensure.prototype.dispatch =
|
||||
function(name, detail) {
|
||||
this.client.executeScript(function(name, detailContent) {
|
||||
var eventDetail = {
|
||||
detail: detailContent
|
||||
};
|
||||
var event = new window.wrappedJSObject.CustomEvent(name, eventDetail);
|
||||
window.wrappedJSObject.dispatchEvent(event);
|
||||
}, [name, detail]);
|
||||
return this;
|
||||
};
|
||||
|
||||
/**
|
||||
* Will call the predicition with named elements.
|
||||
* The current, unamed element would be 'current'.
|
||||
*/
|
||||
Ensure.prototype.must =
|
||||
function(prediction) {
|
||||
this.client.waitFor((function() {
|
||||
return prediction(this.elements);
|
||||
}).bind(this));
|
||||
return this;
|
||||
};
|
||||
|
||||
/**
|
||||
* No busy waiting or throwing error, just return true or false.
|
||||
*/
|
||||
Ensure.prototype.if =
|
||||
function(prediction) {
|
||||
return !!prediction(this.elements);
|
||||
};
|
||||
|
||||
Ensure.prototype.assert =
|
||||
function(prediction) {
|
||||
if (this.if(prediction)) {
|
||||
throw new Error('ASSERTION FAILED');
|
||||
}
|
||||
};
|
||||
|
||||
// Has valid location {x, y}
|
||||
Ensure.prototype.located =
|
||||
function() {
|
||||
if ('number' !== typeof this.elements.current.location().x ||
|
||||
'number' !== typeof this.elements.current.location().y) {
|
||||
throw new Error('The element has no valid location.');
|
||||
}
|
||||
return this;
|
||||
};
|
||||
|
||||
/**
|
||||
* Perform an action chain of MarionetteJS on the element.
|
||||
* Need to decorate all element related methods to partial
|
||||
* apply the element on them.
|
||||
*/
|
||||
Ensure.prototype.actions =
|
||||
function() {
|
||||
var actions = this._actions;
|
||||
// Decorate methods according to different rules.
|
||||
var result = Object.keys(this._actions.__proto__)
|
||||
.reduce((function (acc, fname) {
|
||||
switch (fname) {
|
||||
// These functions need to partial apply the element on it.
|
||||
case 'doubleTap':
|
||||
case 'flick':
|
||||
case 'longPress':
|
||||
case 'move':
|
||||
case 'press':
|
||||
case 'tap':
|
||||
acc[fname] = (function() {
|
||||
var args = Array.prototype.slice.call(arguments);
|
||||
args.unshift(this.elements.current);
|
||||
args = this.centerAction(fname, args);
|
||||
actions[fname].apply(actions, args);
|
||||
this.footprint();
|
||||
return acc;
|
||||
}).bind(this);
|
||||
break;
|
||||
// After perform, need to return to the ensure chain.
|
||||
case 'perform':
|
||||
acc.perform = (function() {
|
||||
actions.perform();
|
||||
return this;
|
||||
}).bind(this);
|
||||
break;
|
||||
default:
|
||||
acc[fname] = (function() {
|
||||
actions[fname].apply(actions, arguments);
|
||||
return acc;
|
||||
}).bind(this);
|
||||
break;
|
||||
}
|
||||
return acc;
|
||||
}).bind(this), {});
|
||||
|
||||
// I have found that flick !== drag & drop...
|
||||
result.pull = (function(offsetX, offsetY) {
|
||||
actions
|
||||
.press(this.elements.current)
|
||||
.moveByOffset(offsetX, offsetY)
|
||||
.release();
|
||||
return result;
|
||||
}).bind(this);
|
||||
return result;
|
||||
};
|
||||
|
||||
/**
|
||||
* Make sure the tapping action would tap at the center of element.
|
||||
* No matter the bug of MarionetteJS get fixed or not, we do our best
|
||||
* to prevent any 'surprise'.
|
||||
*/
|
||||
Ensure.prototype.centerAction =
|
||||
function(actionName, args) {
|
||||
switch (actionName) {
|
||||
case 'tap':
|
||||
case 'press':
|
||||
case 'doubleTap':
|
||||
var element = args[0];
|
||||
var x = args[1];
|
||||
var y = args[2];
|
||||
var box = element.scriptWith(function(element) {
|
||||
var rect = element.getBoundingClientRect();
|
||||
// XXX: we must click on the (1/2 - 1/4) of the
|
||||
// element center on Y axis to make sure it click
|
||||
// to the element. Y at 1/2 would lead to click on
|
||||
// other elements if they're too close.
|
||||
return { x: rect.width >> 1,
|
||||
y: (rect.height >> 1) -
|
||||
(rect.height >> 2) };
|
||||
});
|
||||
if ('undefined' === typeof x) {
|
||||
args[1] = box.x;
|
||||
}
|
||||
if ('undefined' === typeof y) {
|
||||
args[2] = box.y;
|
||||
}
|
||||
break;
|
||||
}
|
||||
return args;
|
||||
};
|
||||
|
||||
Ensure.prototype.patchElement =
|
||||
function(element) {
|
||||
element.position = function() {
|
||||
return element.scriptWith(function(elem) {
|
||||
var box = elem.getBoundingClientRect();
|
||||
return { x: box.x, y: box.y };
|
||||
});
|
||||
};
|
||||
return element;
|
||||
};
|
||||
|
||||
/**
|
||||
* To print and trace states.
|
||||
* cb: function(elements)
|
||||
*/
|
||||
Ensure.prototype.trace =
|
||||
function(cb) {
|
||||
cb(this.elements);
|
||||
return this;
|
||||
};
|
||||
|
||||
/**
|
||||
* Don't know why 'wait' only for action chain,
|
||||
* while 'waitFor' only for Client...
|
||||
*/
|
||||
Ensure.prototype.sleep =
|
||||
function(timeout) {
|
||||
this._actions.wait(timeout).perform();
|
||||
return this;
|
||||
};
|
||||
|
||||
/**
|
||||
* Leave footprint on the current element.
|
||||
*/
|
||||
Ensure.prototype.footprint =
|
||||
function () {
|
||||
this.elements.current.scriptWith(
|
||||
function(element, footprint, footprintCounter) {
|
||||
// Can't use 'dataset[key] = value: Syntax error in MaronetteJS
|
||||
element.setAttribute('data-' + footprint, footprintCounter);
|
||||
}, [this.configs.footprint, this.footprintCounter]);
|
||||
this.footprintCounter += 1;
|
||||
return this;
|
||||
};
|
||||
|
||||
/**
|
||||
* Apply style on all elements have footprints.
|
||||
*/
|
||||
Ensure.prototype.showFootprints =
|
||||
function(stylesOrName, style) {
|
||||
var styles = stylesOrName;
|
||||
var styleFunction;
|
||||
if ('string' === typeof stylesOrName) {
|
||||
if (!style) {
|
||||
throw new Error('No corresponding style for the key: ' + stylesOrName);
|
||||
}
|
||||
styles = {};
|
||||
styles[stylesOrName] = style;
|
||||
} else if ('function' === typeof stylesOrName) {
|
||||
styleFunction = stylesOrName;
|
||||
}
|
||||
var elements = this.client
|
||||
.findElements(this.configs.selectorFootprint);
|
||||
elements.forEach((function(element) {
|
||||
element.scriptWith(function(element, selector, styles, styleFunction) {
|
||||
if (styleFunction) {
|
||||
styleFunction(element);
|
||||
} else {
|
||||
Object.keys(styles).forEach(function(name) {
|
||||
element.style[name] = styles[name];
|
||||
});
|
||||
}
|
||||
}, [this.configs.selectorFootprint, styles, styleFunction]);
|
||||
}).bind(this));
|
||||
return this;
|
||||
};
|
||||
|
||||
module.exports = Ensure;
|
||||
})(module);
|
||||
19
gaia/apps/system/test/marionette/lib/fake_moz_set_message_handler.js
Executable file
19
gaia/apps/system/test/marionette/lib/fake_moz_set_message_handler.js
Executable file
|
|
@ -0,0 +1,19 @@
|
|||
'use strict';
|
||||
|
||||
(function() {
|
||||
var fakeData = null;
|
||||
|
||||
window.wrappedJSObject.__defineGetter__(
|
||||
'mozSetMessageHandler', function() {
|
||||
return function(type) {
|
||||
window.navigator.wrappedJSObject.mozSetMessageHandler(type,
|
||||
function(m) {
|
||||
fakeData = m.data;
|
||||
});
|
||||
};
|
||||
});
|
||||
window.wrappedJSObject.__defineGetter__('__getFakeData', function() {
|
||||
return fakeData;
|
||||
});
|
||||
})();
|
||||
|
||||
71
gaia/apps/system/test/marionette/lib/fake_music.js
Executable file
71
gaia/apps/system/test/marionette/lib/fake_music.js
Executable file
|
|
@ -0,0 +1,71 @@
|
|||
'use strict';
|
||||
|
||||
function FakeMusic(client, origin) {
|
||||
this.client = client;
|
||||
this.origin = origin;
|
||||
}
|
||||
|
||||
module.exports = FakeMusic;
|
||||
|
||||
FakeMusic.Selector = Object.freeze({
|
||||
albumOneElement: '#album-one',
|
||||
playPauseElement: '#play-pause',
|
||||
stopElement: '#stop',
|
||||
previousTrackElement: '#previous',
|
||||
nextTrackElement: '#next',
|
||||
|
||||
interruptElement: '#interrupt'
|
||||
});
|
||||
|
||||
FakeMusic.prototype = {
|
||||
client: null,
|
||||
|
||||
get albumOneElement() {
|
||||
return this.client.findElement(FakeMusic.Selector.albumOneElement);
|
||||
},
|
||||
|
||||
get playPauseElement() {
|
||||
return this.client.findElement(FakeMusic.Selector.playPauseElement);
|
||||
},
|
||||
|
||||
get stopElement() {
|
||||
return this.client.findElement(FakeMusic.Selector.stopElement);
|
||||
},
|
||||
|
||||
get previousTrackElement() {
|
||||
return this.client.findElement(FakeMusic.Selector.previousTrackElement);
|
||||
},
|
||||
|
||||
get nextTrackElement() {
|
||||
return this.client.findElement(FakeMusic.Selector.nextTrackElement);
|
||||
},
|
||||
|
||||
get interruptElement() {
|
||||
return this.client.findElement(FakeMusic.Selector.interruptElement);
|
||||
},
|
||||
|
||||
get isPlaying() {
|
||||
var className = this.playPauseElement.getAttribute('class');
|
||||
return !(/\bis-paused\b/.test(className));
|
||||
},
|
||||
|
||||
launchInBackground: function() {
|
||||
this.client.apps.launch(this.origin);
|
||||
this.client.apps.switchToApp(this.origin);
|
||||
|
||||
// Wait until the app has told us it's fully loaded.
|
||||
this.client.helper.waitForElement('body.loaded');
|
||||
|
||||
this.client.switchToFrame();
|
||||
},
|
||||
|
||||
close: function() {
|
||||
this.client.apps.close(this.origin);
|
||||
},
|
||||
|
||||
runInApp: function(callback) {
|
||||
this.client.apps.switchToApp(this.origin);
|
||||
callback();
|
||||
this.client.switchToFrame();
|
||||
}
|
||||
};
|
||||
40
gaia/apps/system/test/marionette/lib/fakealarmapp.js
Executable file
40
gaia/apps/system/test/marionette/lib/fakealarmapp.js
Executable file
|
|
@ -0,0 +1,40 @@
|
|||
/* global module */
|
||||
'use strict';
|
||||
|
||||
function FakeAlarmApp(client, origin) {
|
||||
this.client = client;
|
||||
this.origin = origin || ('app://' + FakeAlarmApp.DEFAULT_ORIGIN);
|
||||
}
|
||||
|
||||
module.exports = FakeAlarmApp;
|
||||
|
||||
FakeAlarmApp.DEFAULT_ORIGIN = 'fakealarmapp.gaiamobile.org';
|
||||
|
||||
FakeAlarmApp.Selector = Object.freeze({
|
||||
title : '#title'
|
||||
});
|
||||
|
||||
FakeAlarmApp.prototype = {
|
||||
client: null,
|
||||
|
||||
get title() {
|
||||
return this.client.findElement(FakeAlarmApp.Selector.title);
|
||||
},
|
||||
|
||||
launch: function() {
|
||||
this.client.switchToFrame();
|
||||
this.client.apps.launch(this.origin);
|
||||
this.client.apps.switchToApp(this.origin);
|
||||
},
|
||||
|
||||
waitForTitleShown: function(shouldBeShown) {
|
||||
this.client.waitFor(function() {
|
||||
var shown = this.title.displayed();
|
||||
return shown === shouldBeShown;
|
||||
}.bind(this));
|
||||
},
|
||||
|
||||
close: function() {
|
||||
this.client.apps.close(this.origin);
|
||||
}
|
||||
};
|
||||
34
gaia/apps/system/test/marionette/lib/fakeapp.js
Executable file
34
gaia/apps/system/test/marionette/lib/fakeapp.js
Executable file
|
|
@ -0,0 +1,34 @@
|
|||
'use strict';
|
||||
|
||||
function FakeApp(client, origin) {
|
||||
this.client = client;
|
||||
this.origin = origin;
|
||||
}
|
||||
|
||||
module.exports = FakeApp;
|
||||
|
||||
FakeApp.prototype = {
|
||||
client: null,
|
||||
|
||||
get iframe() {
|
||||
return this.getAppIframe(this.origin);
|
||||
},
|
||||
|
||||
launch: function() {
|
||||
this.client.apps.launch(this.origin);
|
||||
|
||||
this.client.apps.switchToApp(this.origin);
|
||||
// Wait until the app has told us it's fully loaded.
|
||||
this.client.helper.waitForElement('body.loaded');
|
||||
|
||||
this.client.switchToFrame();
|
||||
},
|
||||
|
||||
getAppIframe: function(url) {
|
||||
return this.client.findElement('iframe[src*="' + url + '"]');
|
||||
},
|
||||
|
||||
close: function() {
|
||||
this.client.apps.close(this.origin);
|
||||
}
|
||||
};
|
||||
67
gaia/apps/system/test/marionette/lib/fakedialerapp.js
Executable file
67
gaia/apps/system/test/marionette/lib/fakedialerapp.js
Executable file
|
|
@ -0,0 +1,67 @@
|
|||
/* global module */
|
||||
'use strict';
|
||||
|
||||
function FakeDialerApp(client, origin) {
|
||||
this.client = client;
|
||||
this.origin = origin || ('app://' + FakeDialerApp.DEFAULT_ORIGIN);
|
||||
}
|
||||
|
||||
module.exports = FakeDialerApp;
|
||||
|
||||
FakeDialerApp.DEFAULT_ORIGIN = 'fakedialerapp.gaiamobile.org';
|
||||
|
||||
FakeDialerApp.Selector = Object.freeze({
|
||||
title : 'h1'
|
||||
});
|
||||
|
||||
FakeDialerApp.prototype = {
|
||||
client: null,
|
||||
|
||||
get title() {
|
||||
return this.client.findElement(FakeDialerApp.Selector.title);
|
||||
},
|
||||
|
||||
launch: function() {
|
||||
this.client.switchToFrame();
|
||||
this.client.apps.launch(this.origin);
|
||||
this.client.apps.switchToApp(this.origin);
|
||||
},
|
||||
|
||||
waitForTitleShown: function(shouldBeShown) {
|
||||
this.client.waitFor(function() {
|
||||
var shown = this.title.displayed();
|
||||
return shown === shouldBeShown;
|
||||
}.bind(this));
|
||||
},
|
||||
|
||||
getCallHeight: function() {
|
||||
this.client.switchToFrame();
|
||||
var oncallframe =
|
||||
this.client.helper.waitForElement('.attentionWindow.active iframe');
|
||||
this.client.switchToFrame(oncallframe);
|
||||
return this.client.executeScript(function() {
|
||||
return window.wrappedJSObject.innerHeight;
|
||||
});
|
||||
},
|
||||
|
||||
focusAndWaitForResize: function() {
|
||||
this.client.switchToFrame();
|
||||
var oncallframe =
|
||||
this.client.helper.waitForElement('.attentionWindow.active iframe');
|
||||
this.client.switchToFrame(oncallframe);
|
||||
|
||||
this.client.findElement('#input').click();
|
||||
|
||||
this.client.executeAsyncScript(function() {
|
||||
var win = window.wrappedJSObject;
|
||||
win.addEventListener('resize', function resizeWait() {
|
||||
win.removeEventListener('resize', resizeWait);
|
||||
marionetteScriptFinished();
|
||||
});
|
||||
});
|
||||
},
|
||||
|
||||
close: function() {
|
||||
this.client.apps.close(this.origin);
|
||||
}
|
||||
};
|
||||
40
gaia/apps/system/test/marionette/lib/fakeglobaloverlayapp.js
Executable file
40
gaia/apps/system/test/marionette/lib/fakeglobaloverlayapp.js
Executable file
|
|
@ -0,0 +1,40 @@
|
|||
/* global module */
|
||||
'use strict';
|
||||
|
||||
function FakeGlobalOverlayApp(client, origin) {
|
||||
this.client = client;
|
||||
this.origin = origin || ('app://' + FakeGlobalOverlayApp.DEFAULT_ORIGIN);
|
||||
}
|
||||
|
||||
module.exports = FakeGlobalOverlayApp;
|
||||
|
||||
FakeGlobalOverlayApp.DEFAULT_ORIGIN = 'fakeglobaloverlayapp.gaiamobile.org';
|
||||
|
||||
FakeGlobalOverlayApp.Selector = Object.freeze({
|
||||
title : 'h3'
|
||||
});
|
||||
|
||||
FakeGlobalOverlayApp.prototype = {
|
||||
client: null,
|
||||
|
||||
get title() {
|
||||
return this.client.findElement(FakeGlobalOverlayApp.Selector.title);
|
||||
},
|
||||
|
||||
launch: function() {
|
||||
this.client.switchToFrame();
|
||||
this.client.apps.launch(this.origin);
|
||||
this.client.apps.switchToApp(this.origin);
|
||||
},
|
||||
|
||||
waitForTitleShown: function(shouldBeShown) {
|
||||
this.client.waitFor(function() {
|
||||
var shown = this.title.displayed();
|
||||
return shown === shouldBeShown;
|
||||
}.bind(this));
|
||||
},
|
||||
|
||||
close: function() {
|
||||
this.client.apps.close(this.origin);
|
||||
}
|
||||
};
|
||||
42
gaia/apps/system/test/marionette/lib/fakeglobaloverlayapp_nopermissions.js
Executable file
42
gaia/apps/system/test/marionette/lib/fakeglobaloverlayapp_nopermissions.js
Executable file
|
|
@ -0,0 +1,42 @@
|
|||
/* global module */
|
||||
'use strict';
|
||||
|
||||
function FakeGlobalOverlayAppNoPerms(client, origin) {
|
||||
this.client = client;
|
||||
this.origin = origin ||
|
||||
('app://' + FakeGlobalOverlayAppNoPerms.DEFAULT_ORIGIN);
|
||||
}
|
||||
|
||||
module.exports = FakeGlobalOverlayAppNoPerms;
|
||||
|
||||
FakeGlobalOverlayAppNoPerms.DEFAULT_ORIGIN =
|
||||
'fakeglobaloverlayapp_noperms.gaiamobile.org';
|
||||
|
||||
FakeGlobalOverlayAppNoPerms.Selector = Object.freeze({
|
||||
title : 'h3'
|
||||
});
|
||||
|
||||
FakeGlobalOverlayAppNoPerms.prototype = {
|
||||
client: null,
|
||||
|
||||
get title() {
|
||||
return this.client.findElement(FakeGlobalOverlayAppNoPerms.Selector.title);
|
||||
},
|
||||
|
||||
launch: function() {
|
||||
this.client.switchToFrame();
|
||||
this.client.apps.launch(this.origin);
|
||||
this.client.apps.switchToApp(this.origin);
|
||||
},
|
||||
|
||||
waitForTitleShown: function(shouldBeShown) {
|
||||
this.client.waitFor(function() {
|
||||
var shown = this.title.displayed();
|
||||
return shown === shouldBeShown;
|
||||
}.bind(this));
|
||||
},
|
||||
|
||||
close: function() {
|
||||
this.client.apps.close(this.origin);
|
||||
}
|
||||
};
|
||||
50
gaia/apps/system/test/marionette/lib/fakeloopapp.js
Executable file
50
gaia/apps/system/test/marionette/lib/fakeloopapp.js
Executable file
|
|
@ -0,0 +1,50 @@
|
|||
/* global module */
|
||||
'use strict';
|
||||
|
||||
function FakeLoopApp(client, origin) {
|
||||
this.client = client;
|
||||
this.origin = origin || ('app://' + FakeLoopApp.DEFAULT_ORIGIN);
|
||||
}
|
||||
|
||||
module.exports = FakeLoopApp;
|
||||
|
||||
FakeLoopApp.DEFAULT_ORIGIN = 'fakeloopapp.gaiamobile.org';
|
||||
|
||||
FakeLoopApp.Selector = Object.freeze({
|
||||
title: 'h1',
|
||||
toaster: '.attentionWindow.toaster-mode[toaster-transition-state="opened"]',
|
||||
mainWindow: '.attentionWindow.top-most.active'
|
||||
});
|
||||
|
||||
FakeLoopApp.prototype = {
|
||||
client: null,
|
||||
|
||||
get title() {
|
||||
return this.client.findElement(FakeLoopApp.Selector.title);
|
||||
},
|
||||
|
||||
get toaster() {
|
||||
return this.client.helper.waitForElement(FakeLoopApp.Selector.toaster);
|
||||
},
|
||||
|
||||
get mainWindow() {
|
||||
return this.client.helper.waitForElement(FakeLoopApp.Selector.mainWindow);
|
||||
},
|
||||
|
||||
launch: function() {
|
||||
this.client.switchToFrame();
|
||||
this.client.apps.launch(this.origin);
|
||||
this.client.apps.switchToApp(this.origin);
|
||||
},
|
||||
|
||||
waitForTitleShown: function(shouldBeShown) {
|
||||
this.client.waitFor(function() {
|
||||
var shown = this.title.displayed();
|
||||
return shown === shouldBeShown;
|
||||
}.bind(this));
|
||||
},
|
||||
|
||||
close: function() {
|
||||
this.client.apps.close(this.origin);
|
||||
}
|
||||
};
|
||||
40
gaia/apps/system/test/marionette/lib/fakemediaapp.js
Executable file
40
gaia/apps/system/test/marionette/lib/fakemediaapp.js
Executable file
|
|
@ -0,0 +1,40 @@
|
|||
/* global module */
|
||||
'use strict';
|
||||
|
||||
function FakeMediaApp(client, origin) {
|
||||
this.client = client;
|
||||
this.origin = origin || ('app://' + FakeMediaApp.DEFAULT_ORIGIN);
|
||||
}
|
||||
|
||||
module.exports = FakeMediaApp;
|
||||
|
||||
FakeMediaApp.DEFAULT_ORIGIN = 'fakemediaapp.gaiamobile.org';
|
||||
|
||||
FakeMediaApp.Selector = Object.freeze({
|
||||
title : '#title'
|
||||
});
|
||||
|
||||
FakeMediaApp.prototype = {
|
||||
client: null,
|
||||
|
||||
get title() {
|
||||
return this.client.findElement(FakeMediaApp.Selector.title);
|
||||
},
|
||||
|
||||
launch: function() {
|
||||
this.client.switchToFrame();
|
||||
this.client.apps.launch(this.origin);
|
||||
this.client.apps.switchToApp(this.origin);
|
||||
},
|
||||
|
||||
waitForTitleShown: function(shouldBeShown) {
|
||||
this.client.waitFor(function() {
|
||||
var shown = this.title.displayed();
|
||||
return shown === shouldBeShown;
|
||||
}.bind(this));
|
||||
},
|
||||
|
||||
close: function() {
|
||||
this.client.apps.close(this.origin);
|
||||
}
|
||||
};
|
||||
40
gaia/apps/system/test/marionette/lib/fakenotificationapp.js
Executable file
40
gaia/apps/system/test/marionette/lib/fakenotificationapp.js
Executable file
|
|
@ -0,0 +1,40 @@
|
|||
/* global module */
|
||||
'use strict';
|
||||
|
||||
function FakeNotificationApp(client, origin) {
|
||||
this.client = client;
|
||||
this.origin = origin || ('app://' + FakeNotificationApp.DEFAULT_ORIGIN);
|
||||
}
|
||||
|
||||
module.exports = FakeNotificationApp;
|
||||
|
||||
FakeNotificationApp.DEFAULT_ORIGIN = 'fakenotificationapp.gaiamobile.org';
|
||||
|
||||
FakeNotificationApp.Selector = Object.freeze({
|
||||
title : '#title'
|
||||
});
|
||||
|
||||
FakeNotificationApp.prototype = {
|
||||
client: null,
|
||||
|
||||
get title() {
|
||||
return this.client.findElement(FakeNotificationApp.Selector.title);
|
||||
},
|
||||
|
||||
launch: function() {
|
||||
this.client.switchToFrame();
|
||||
this.client.apps.launch(this.origin);
|
||||
this.client.apps.switchToApp(this.origin);
|
||||
},
|
||||
|
||||
waitForTitleShown: function(shouldBeShown) {
|
||||
this.client.waitFor(function() {
|
||||
var shown = this.title.displayed();
|
||||
return shown === shouldBeShown;
|
||||
}.bind(this));
|
||||
},
|
||||
|
||||
close: function() {
|
||||
this.client.apps.close(this.origin);
|
||||
}
|
||||
};
|
||||
195
gaia/apps/system/test/marionette/lib/faketextselectionapp.js
Executable file
195
gaia/apps/system/test/marionette/lib/faketextselectionapp.js
Executable file
|
|
@ -0,0 +1,195 @@
|
|||
'use strict';
|
||||
|
||||
var TextSelection = require('./text_selection');
|
||||
var SelectionHelper = require('./selection_helper');
|
||||
|
||||
function FakeTextSelectionApp(client) {
|
||||
this.client = client;
|
||||
|
||||
this.client.apps.launch(FakeTextSelectionApp.ORIGIN);
|
||||
this.client.apps.switchToApp(FakeTextSelectionApp.ORIGIN);
|
||||
|
||||
this.textSelection = new TextSelection(this.client);
|
||||
}
|
||||
|
||||
module.exports = FakeTextSelectionApp;
|
||||
|
||||
FakeTextSelectionApp.Selector = Object.freeze({
|
||||
// dialogposition.html
|
||||
DialogPositionBottomLeftInput: '#bottom-left-input',
|
||||
DialogPositionBottomRightInput: '#bottom-right-input',
|
||||
DialogPositionCenterInput: '#center-input',
|
||||
DialogPositionTopLeftInput: '#top-left-input',
|
||||
DialogPositionTopRightInput: '#top-right-input',
|
||||
// functionality.html
|
||||
FunctionalitySourceInput: '#functionality-source',
|
||||
FunctionalityTargetInput: '#functionality-target',
|
||||
// noneditable.html
|
||||
NonEditableCenterInput: '#ne-center-input',
|
||||
NonEditableNormalDiv: '#noneditable',
|
||||
NonEditableNonSelectedDiv: '#noneditable-userselectnone',
|
||||
// bug.html
|
||||
BugCenterInput: '#bug-center-input',
|
||||
BugBottomInput: '#bug-bottom-input',
|
||||
BugNormalDiv: '#bug-normal-div',
|
||||
// bug1120358.html
|
||||
BugContent: '#bug-content',
|
||||
// bug1120316.html
|
||||
BugInput: '#bug-input',
|
||||
BugTextarea: '#bug-textarea',
|
||||
});
|
||||
|
||||
FakeTextSelectionApp.ORIGIN = 'app://faketextselectionapp.gaiamobile.org';
|
||||
|
||||
FakeTextSelectionApp.prototype = {
|
||||
get bubbleVisiblity() {
|
||||
return this.textSelection.visibility;
|
||||
},
|
||||
|
||||
get width() {
|
||||
return this.client.executeScript(function() {
|
||||
return window.wrappedJSObject.innerWidth;
|
||||
});
|
||||
},
|
||||
|
||||
get DialogPositionCenterInput() {
|
||||
return this._getElement('DialogPositionCenterInput');
|
||||
},
|
||||
|
||||
get DialogPositionBottomLeftInput() {
|
||||
return this._getElement('DialogPositionBottomLeftInput');
|
||||
},
|
||||
|
||||
get DialogPositionBottomRightInput() {
|
||||
return this._getElement('DialogPositionBottomRightInput');
|
||||
},
|
||||
|
||||
get DialogPositionTopLeftInput() {
|
||||
return this._getElement('DialogPositionTopLeftInput');
|
||||
},
|
||||
|
||||
get DialogPositionTopRightInput() {
|
||||
return this._getElement('DialogPositionTopRightInput');
|
||||
},
|
||||
|
||||
get FunctionalitySourceInput() {
|
||||
return this._getElement('FunctionalitySourceInput');
|
||||
},
|
||||
|
||||
get FunctionalityTargetInput() {
|
||||
return this._getElement('FunctionalityTargetInput');
|
||||
},
|
||||
|
||||
get NonEditableCenterInput() {
|
||||
return this._getElement('NonEditableCenterInput');
|
||||
},
|
||||
|
||||
get NonEditableNormalDiv() {
|
||||
return this._getElement('NonEditableNormalDiv');
|
||||
},
|
||||
|
||||
get NonEditableNonSelectedDiv() {
|
||||
return this._getElement('NonEditableNonSelectedDiv');
|
||||
},
|
||||
|
||||
get BugCenterInput() {
|
||||
return this._getElement('BugCenterInput');
|
||||
},
|
||||
|
||||
get BugButtomInput() {
|
||||
return this._getElement('BugButtomInput');
|
||||
},
|
||||
|
||||
get BugNormalDiv() {
|
||||
return this._getElement('BugNormalDiv');
|
||||
},
|
||||
|
||||
get BugContent() {
|
||||
return this._getElement('BugContent');
|
||||
},
|
||||
|
||||
get BugInput() {
|
||||
return this._getElement('BugInput');
|
||||
},
|
||||
|
||||
get BugTextarea() {
|
||||
return this._getElement('BugTextarea');
|
||||
},
|
||||
|
||||
_getElement: function(target) {
|
||||
var element = this.client.helper.waitForElement(
|
||||
FakeTextSelectionApp.Selector[target]);
|
||||
element.location = element.location();
|
||||
element.selectionHelper = new SelectionHelper(this.client, element);
|
||||
return element;
|
||||
},
|
||||
|
||||
/**
|
||||
*
|
||||
* Show text selection dialog on element.
|
||||
*
|
||||
* @param {String} ele query string of dom element.
|
||||
*/
|
||||
longPress: function(target) {
|
||||
var dom = this.client.helper.waitForElement(
|
||||
FakeTextSelectionApp.Selector[target]);
|
||||
// wait for keyboard showing up first
|
||||
this.textSelection.longPress(dom);
|
||||
},
|
||||
|
||||
longPressByPosition: function(target, x, y) {
|
||||
var dom = this.client.helper.waitForElement(
|
||||
FakeTextSelectionApp.Selector[target]);
|
||||
// wait for keyboard showing up first
|
||||
this.textSelection.longPressByPosition(dom, x, y);
|
||||
},
|
||||
|
||||
copy: function(fromEle) {
|
||||
this.longPress(fromEle);
|
||||
this.textSelection.pressCopy();
|
||||
},
|
||||
|
||||
paste: function(toEle) {
|
||||
this.longPress(toEle);
|
||||
this.textSelection.pressPaste();
|
||||
},
|
||||
|
||||
cut: function(fromEle) {
|
||||
this.longPress(fromEle);
|
||||
this.textSelection.pressCut();
|
||||
},
|
||||
|
||||
// Copy element from fromEle and paste it to toEle.
|
||||
copyTo: function(fromEle, toEle) {
|
||||
this.copy(fromEle);
|
||||
this.paste(toEle);
|
||||
},
|
||||
|
||||
// Cut element from fromEle and paste it to toEle.
|
||||
cutTo: function(fromEle, toEle) {
|
||||
this.cut(fromEle);
|
||||
this.paste(toEle);
|
||||
},
|
||||
|
||||
// Select all of ele and cut it.
|
||||
selectAllAndCut: function(ele) {
|
||||
this.longPress(ele);
|
||||
this.textSelection.pressSelectAll();
|
||||
this.textSelection.pressCut();
|
||||
},
|
||||
|
||||
selectAll: function(ele) {
|
||||
this.longPress(ele);
|
||||
this.textSelection.pressSelectAll();
|
||||
},
|
||||
|
||||
setTestFrame: function(frameName) {
|
||||
this.client.executeScript(function(frameName) {
|
||||
window.wrappedJSObject.location.href = '/' + frameName + '.html';
|
||||
}, [frameName]);
|
||||
},
|
||||
|
||||
switchToTestApp: function() {
|
||||
this.textSelection.switchToCurrentApp();
|
||||
}
|
||||
};
|
||||
24
gaia/apps/system/test/marionette/lib/fullscreen_app.js
Executable file
24
gaia/apps/system/test/marionette/lib/fullscreen_app.js
Executable file
|
|
@ -0,0 +1,24 @@
|
|||
'use strict';
|
||||
|
||||
function FullScreenApp(client, origin) {
|
||||
this.client = client;
|
||||
this.origin = origin;
|
||||
}
|
||||
|
||||
module.exports = FullScreenApp;
|
||||
|
||||
FullScreenApp.prototype = {
|
||||
client: null,
|
||||
|
||||
launch: function() {
|
||||
this.client.apps.launch(this.origin);
|
||||
this.client.apps.switchToApp(this.origin);
|
||||
|
||||
// Wait until the app has told us it's fully loaded.
|
||||
this.client.helper.waitForElement('body.loaded');
|
||||
},
|
||||
|
||||
close: function() {
|
||||
this.client.apps.close(this.origin);
|
||||
}
|
||||
};
|
||||
74
gaia/apps/system/test/marionette/lib/fxa_system_dialog.js
Executable file
74
gaia/apps/system/test/marionette/lib/fxa_system_dialog.js
Executable file
|
|
@ -0,0 +1,74 @@
|
|||
'use strict';
|
||||
|
||||
function FxASystemDialog(client) {
|
||||
this.client = client.scope({
|
||||
searchTimeout: 20000
|
||||
});
|
||||
}
|
||||
|
||||
module.exports = FxASystemDialog;
|
||||
|
||||
FxASystemDialog.prototype = {
|
||||
client: null,
|
||||
|
||||
show: function() {
|
||||
this.client.switchToFrame();
|
||||
this.client.executeAsyncScript(function() {
|
||||
var win = window.wrappedJSObject;
|
||||
win.document.getElementById('fxa-dialog').addEventListener('animationend',
|
||||
function wait() {
|
||||
win.document.getElementById('fxa-dialog')
|
||||
.removeEventListener('animationend', wait);
|
||||
marionetteScriptFinished();
|
||||
});
|
||||
window.wrappedJSObject.Service.request('FxAccountsUI:login');
|
||||
});
|
||||
},
|
||||
|
||||
getHeight: function() {
|
||||
this.client.switchToFrame();
|
||||
this.client.switchToFrame(this.client.findElement('#fxa-iframe'));
|
||||
return this.client.executeScript(function() {
|
||||
return window.wrappedJSObject.innerHeight;
|
||||
});
|
||||
},
|
||||
|
||||
typeEmail: function(email) {
|
||||
this.client.switchToFrame();
|
||||
this.client.switchToFrame(this.client.findElement('#fxa-iframe'));
|
||||
this.client.findElement('#fxa-email-input').sendKeys(email);
|
||||
this.client.executeScript(function() {
|
||||
window.wrappedJSObject.document.getElementById('fxa-email-input').blur();
|
||||
});
|
||||
this.client.switchToFrame();
|
||||
this.client.helper.waitForElementToDisappear(
|
||||
this.client.findElement('#keyboards .inputWindow.top-most'));
|
||||
},
|
||||
|
||||
goToCOPPA: function() {
|
||||
this.client.switchToFrame();
|
||||
this.client.executeScript(function() {
|
||||
window.wrappedJSObject.document.getElementById('fxa-iframe')
|
||||
.contentWindow.location.hash = 'fxa-coppa';
|
||||
});
|
||||
},
|
||||
|
||||
focus: function() {
|
||||
this.client.switchToFrame();
|
||||
this.client.switchToFrame(this.client.findElement('#fxa-iframe'));
|
||||
this.client.findElement('#fxa-email-input').click();
|
||||
},
|
||||
|
||||
focusAge: function() {
|
||||
this.client.switchToFrame();
|
||||
this.client.switchToFrame(this.client.findElement('#fxa-iframe'));
|
||||
this.client.helper.waitForElement('#fxa-age-select').click();
|
||||
},
|
||||
|
||||
hide: function() {
|
||||
this.client.switchToFrame();
|
||||
this.client.executeScript(function() {
|
||||
window.wrappedJSObject.FxAccountsUI.close();
|
||||
});
|
||||
}
|
||||
};
|
||||
123
gaia/apps/system/test/marionette/lib/input_management.js
Executable file
123
gaia/apps/system/test/marionette/lib/input_management.js
Executable file
|
|
@ -0,0 +1,123 @@
|
|||
'use strict';
|
||||
|
||||
/**
|
||||
* A helper module for the input management framework in system app.
|
||||
*/
|
||||
|
||||
var Marionette = require('marionette-client');
|
||||
|
||||
function InputManagement(client) {
|
||||
this.client = client.scope({ searchTimeout: 20000 });
|
||||
this.actions = new Marionette.Actions(client);
|
||||
}
|
||||
|
||||
module.exports = InputManagement;
|
||||
|
||||
// Selectors for the DOM in system app.
|
||||
InputManagement.Selector = Object.freeze({
|
||||
inputWindows: '#keyboards .inputWindow',
|
||||
imeMenu: 'form.ime-menu',
|
||||
imeNotification: '#keyboard-show-ime-list'
|
||||
});
|
||||
|
||||
InputManagement.prototype = {
|
||||
// getters for DOM elements in keyboard app
|
||||
get inputWindows() {
|
||||
return this.client.findElements(InputManagement.Selector.inputWindows);
|
||||
},
|
||||
|
||||
get imeMenu() {
|
||||
return this.client.findElement(InputManagement.Selector.imeMenu);
|
||||
},
|
||||
|
||||
get imeNotification() {
|
||||
return this.client.findElement(InputManagement.Selector.imeNotification);
|
||||
},
|
||||
|
||||
/**
|
||||
* Wait for keyboard frame trasition end.
|
||||
*/
|
||||
waitForKeyboardFrameDisplayed: function() {
|
||||
this.client.waitFor(() => {
|
||||
return this.keyboardFrameDisplayed();
|
||||
});
|
||||
},
|
||||
|
||||
waitForKeyboardFrameHidden: function() {
|
||||
this.client.waitFor(() => {
|
||||
return this.keyboardFrameHidden();
|
||||
});
|
||||
},
|
||||
|
||||
keyboardFrameDisplayed: function() {
|
||||
// Switch to System app.
|
||||
this.client.switchToFrame();
|
||||
|
||||
// Wait for the keyboard pop up and switch to it.
|
||||
// This is satisfied as long as one inputWindow pops up.
|
||||
var inputWindows = this.inputWindows;
|
||||
|
||||
var expectedTransform = 'matrix(1, 0, 0, 1, 0, 0)';
|
||||
|
||||
return inputWindows.some(function(inputWindow) {
|
||||
var currentTransform = inputWindow.cssProperty('transform');
|
||||
return (currentTransform === expectedTransform);
|
||||
});
|
||||
},
|
||||
|
||||
keyboardFrameHidden: function() {
|
||||
// Switch to System app.
|
||||
this.client.switchToFrame();
|
||||
|
||||
// This is satisfied only when all inputWindows are regarded as
|
||||
// having slided to the bottom.
|
||||
var inputWindows = this.inputWindows;
|
||||
|
||||
return inputWindows.every(function(inputWindow) {
|
||||
var height = inputWindow.scriptWith(function(frame) {
|
||||
return frame.clientHeight;
|
||||
});
|
||||
|
||||
var currentTransform = inputWindow.cssProperty('transform');
|
||||
var expectedTransform = 'matrix(1, 0, 0, 1, 0, '+ height +')';
|
||||
|
||||
return (currentTransform === expectedTransform);
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* Switch to active keyboard frame.
|
||||
*/
|
||||
switchToActiveKeyboardFrame: function() {
|
||||
var client = this.client;
|
||||
// Don't call switchToApp here, since keyboard frame itself would not
|
||||
// follow the rule of AppWindow.
|
||||
var keyboardFrame =
|
||||
client.findElement('#keyboards .inputWindow.active iframe');
|
||||
client.switchToFrame(keyboardFrame);
|
||||
},
|
||||
|
||||
selectImeOption: function selectImeOption(index) {
|
||||
var imeOption = this.imeMenu.findElement('li[data-id="' + index + '"]');
|
||||
imeOption.click();
|
||||
},
|
||||
|
||||
dragDownUtilityTray: function dragDownUtilityTray() {
|
||||
this.client.switchToFrame();
|
||||
|
||||
this.client.executeScript(function() {
|
||||
window.wrappedJSObject.UtilityTray.show(/* immediately: */ true);
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* Ensure that the number of input windows is >= the given count.
|
||||
*/
|
||||
ensureInputWindowCount: function(count) {
|
||||
// wait for the 2nd keyboard is loaded
|
||||
var inputWindows = this.inputWindows;
|
||||
this.client.waitFor(function() {
|
||||
return (inputWindows.length >= count);
|
||||
});
|
||||
}
|
||||
};
|
||||
268
gaia/apps/system/test/marionette/lib/lockscreen.js
Executable file
268
gaia/apps/system/test/marionette/lib/lockscreen.js
Executable file
|
|
@ -0,0 +1,268 @@
|
|||
/**
|
||||
* To provide a abstract LockScreen, thus we can change the implementation
|
||||
* to get closer with user's behavior (unlock via sliding; lock with button).
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
var Promise = require('es6-promise').Promise; // jshint ignore:line
|
||||
|
||||
(function(module) {
|
||||
var LockScreen = function() {};
|
||||
|
||||
LockScreen.prototype.start = function(client) {
|
||||
this.Ensure = require('./ensure.js');
|
||||
this.client = client;
|
||||
this.selector = {
|
||||
lockSlider: '#lockscreen-icon-container'
|
||||
};
|
||||
// XXX: After we make LockScree as an iframe or app, we need this to
|
||||
// indicate to switch to which frame.
|
||||
this.lockScreenFrameOrigin = 'app://lockscreen.gaiamobile.org';
|
||||
this.decorateMethods();
|
||||
return this;
|
||||
};
|
||||
|
||||
// Do AOP things: add switch to System frame before executing every method.
|
||||
// This is because LockScreen commands should be fired via System,
|
||||
// to avoid switching to wrong frame without proper permissions and components
|
||||
LockScreen.prototype.decorateMethods =
|
||||
function() {
|
||||
Object.keys(LockScreen.prototype).forEach((function(methodName) {
|
||||
if ('decorateMethods' === methodName ||
|
||||
'ensure' === methodName) {
|
||||
return;
|
||||
}
|
||||
var method = this[methodName];
|
||||
this[methodName] = (function() {
|
||||
this.ensure().frame();
|
||||
method.apply(this, arguments);
|
||||
}).bind(this);
|
||||
}).bind(this));
|
||||
};
|
||||
|
||||
LockScreen.prototype.ensure =
|
||||
function() {
|
||||
var ensure = (new this.Ensure()).start(this.client);
|
||||
return ensure;
|
||||
};
|
||||
|
||||
LockScreen.prototype.setEnable =
|
||||
function(value) {
|
||||
this.ensure().settings();
|
||||
this.client.executeAsyncScript(function(value) {
|
||||
var settings = window.wrappedJSObject.navigator.mozSettings;
|
||||
var lock = settings.createLock();
|
||||
var result = lock.set({
|
||||
'lockscreen.enabled': value
|
||||
});
|
||||
result.onsuccess = function() {
|
||||
result = null;
|
||||
lock = null;
|
||||
settings = null;
|
||||
marionetteScriptFinished();
|
||||
};
|
||||
result.onerror = function() {
|
||||
marionetteScriptFinished();
|
||||
throw new Error('Cannot set LockScreen as ' + value);
|
||||
};
|
||||
}, [value]);
|
||||
};
|
||||
|
||||
LockScreen.prototype.getEnable =
|
||||
function(cb) {
|
||||
this.ensure().settings();
|
||||
this.client.executeAsyncScript(function() {
|
||||
var settings = window.wrappedJSObject.navigator.mozSettings;
|
||||
var lock = settings.createLock();
|
||||
var result = lock.get('lockscreen.enabled');
|
||||
result.onerror = function() {
|
||||
throw new Error('Cannot get LockScreen enabled value');
|
||||
};
|
||||
result.onsuccess = function() {
|
||||
marionetteScriptFinished(result.result);
|
||||
result = null;
|
||||
lock = null;
|
||||
settings = null;
|
||||
};
|
||||
}, cb);
|
||||
};
|
||||
|
||||
LockScreen.prototype.setPasscode =
|
||||
function(_passcode, cb) {
|
||||
this.ensure().settings();
|
||||
this.client.executeAsyncScript(function(passcode) {
|
||||
try {
|
||||
window.wrappedJSObject.PasscodeHelper.set(passcode);
|
||||
} catch(e) {
|
||||
throw e;
|
||||
}
|
||||
var settings = window.wrappedJSObject.navigator.mozSettings;
|
||||
var lock = settings.createLock();
|
||||
lock.set({
|
||||
'lockscreen.passcode-lock.enabled': true
|
||||
}).then((function() {
|
||||
marionetteScriptFinished();
|
||||
}).bind(this)).catch(function(e) {
|
||||
throw new Error('Cannot set LockScreen passcode enabled value');
|
||||
});
|
||||
}, [_passcode], (function() {
|
||||
// To end the flow when we get the value.
|
||||
// XXX: In fact, it doesn't matter whether the value is true,
|
||||
// since it's always true. The mysterious error is if we don't
|
||||
// check the value, the set action won't be executed.
|
||||
// This is an issues that the reason and symptom is still unknown.
|
||||
this.checkPasscodeEnabled(cb);
|
||||
}).bind(this));
|
||||
};
|
||||
|
||||
LockScreen.prototype.checkPasscodeEnabled =
|
||||
function(cb) {
|
||||
this.client.executeAsyncScript(function() {
|
||||
var settings = window.wrappedJSObject.navigator.mozSettings;
|
||||
var lock = settings.createLock();
|
||||
lock.get('lockscreen.passcode-lock.enabled')
|
||||
.then(function(result) {
|
||||
marionetteScriptFinished(result['lockscreen.passcode-lock.enabled']);
|
||||
}).catch(function(e) {
|
||||
throw new Error('Cannot get LockScreen passcode enabled value');
|
||||
});
|
||||
}, [], cb);
|
||||
};
|
||||
|
||||
LockScreen.prototype.disable =
|
||||
function() {
|
||||
this.setEnable(false);
|
||||
return this;
|
||||
};
|
||||
|
||||
LockScreen.prototype.enable =
|
||||
function() {
|
||||
this.setEnable(true);
|
||||
return this;
|
||||
};
|
||||
|
||||
// XXX: Marionette client has a bug: LockScreen launched during
|
||||
// booting would be broken. Need to relock after it to do
|
||||
// all LockScreen test.
|
||||
//
|
||||
// To do a test with LockScreen, disable it in the primary settings
|
||||
// passed to the client, and then call this method to relock it.
|
||||
LockScreen.prototype.relock =
|
||||
function() {
|
||||
this.setEnable(true);
|
||||
this.lock();
|
||||
return this;
|
||||
};
|
||||
|
||||
// Would lock the screen and switch to the frame.
|
||||
LockScreen.prototype.lock =
|
||||
function() {
|
||||
this.getEnable(function(err, value) {
|
||||
if (err) {
|
||||
throw err;
|
||||
} else if (!value) {
|
||||
throw new Error('Cannot lock while it\'s disable');
|
||||
}
|
||||
});
|
||||
|
||||
this.client.switchToFrame();
|
||||
this.client.executeScript(function() {
|
||||
window.wrappedJSObject.Service.request('lock', {
|
||||
forcibly: true
|
||||
});
|
||||
});
|
||||
this.ensure()
|
||||
.must((function() {
|
||||
try {
|
||||
// Make sure the frame is displayed.
|
||||
this.ensure().frame(this.lockScreenFrameOrigin);
|
||||
return true;
|
||||
} catch (e) {
|
||||
return false;
|
||||
}
|
||||
}).bind(this))
|
||||
.frame(this.lockScreenFrameOrigin);
|
||||
return this;
|
||||
};
|
||||
|
||||
// Would unlock the screen and switch to the System frame.
|
||||
LockScreen.prototype.unlock =
|
||||
function() {
|
||||
this.ensure().frame();
|
||||
this.client.executeScript(function() {
|
||||
window.wrappedJSObject.Service.request('unlock', {
|
||||
forcibly: true
|
||||
});
|
||||
});
|
||||
this.ensure()
|
||||
.must((function() {
|
||||
try {
|
||||
// Make sure the LockScreen frame is not displayed.
|
||||
this.ensure().frame(this.lockScreenFrameOrigin);
|
||||
return false;
|
||||
} catch (e) {
|
||||
return true;
|
||||
}
|
||||
}).bind(this));
|
||||
return this;
|
||||
};
|
||||
|
||||
// Slide to unlock the screen
|
||||
LockScreen.prototype.slideToUnlock =
|
||||
function(cb) {
|
||||
this._slideLockTo('right', cb);
|
||||
};
|
||||
|
||||
// Slide to open camera app
|
||||
LockScreen.prototype.slideToOpenCamera =
|
||||
function(cb) {
|
||||
this._slideLockTo('left', cb);
|
||||
};
|
||||
|
||||
|
||||
// Wait new one from the time being.
|
||||
// Will change the focusing frame to System.
|
||||
// `toHowMany` is optional.
|
||||
LockScreen.prototype.waitForNotificationChange =
|
||||
function(toHowMany) {
|
||||
var currentNumber = this.client
|
||||
.findElements('#notifications-lockscreen-container .notification').length;
|
||||
toHowMany = toHowMany || currentNumber + 1;
|
||||
this.client.switchToFrame();
|
||||
this.client.waitFor((function() {
|
||||
var nowWeHave = this.client
|
||||
.findElements('#notifications-lockscreen-container .notification')
|
||||
.length;
|
||||
return toHowMany === nowWeHave;
|
||||
}).bind(this));
|
||||
};
|
||||
|
||||
// Slide an element given x and y offsets
|
||||
LockScreen.prototype._slideByOffset =
|
||||
function(element, x, y, cb) {
|
||||
var actions = this.client.loader.getActions();
|
||||
// actions.flick doesn't work for some reason. Resorted to breaking it
|
||||
// down to press > move > release. The waiting time in between each action
|
||||
// is necessary.
|
||||
actions.press(element).wait(0.5).moveByOffset(x, y).wait(0.5).release().
|
||||
perform(cb);
|
||||
};
|
||||
|
||||
// Slide lockscreen to left or right
|
||||
LockScreen.prototype._slideLockTo =
|
||||
function(direction, cb) {
|
||||
this.ensure().frame();
|
||||
var lockSlider = this.client.findElement(this.selector.lockSlider);
|
||||
var size = lockSlider.size();
|
||||
switch (direction) {
|
||||
case 'left':
|
||||
this._slideByOffset(lockSlider, -size.width / 2, 0, cb);
|
||||
break;
|
||||
case 'right':
|
||||
this._slideByOffset(lockSlider, size.width / 2, 0, cb);
|
||||
break;
|
||||
}
|
||||
};
|
||||
|
||||
module.exports = LockScreen;
|
||||
})(module);
|
||||
114
gaia/apps/system/test/marionette/lib/lockscreen_media_playback_actions.js
Executable file
114
gaia/apps/system/test/marionette/lib/lockscreen_media_playback_actions.js
Executable file
|
|
@ -0,0 +1,114 @@
|
|||
'use strict';
|
||||
(function(module) {
|
||||
|
||||
var LockScreenMediaPlaybackActions = function() {};
|
||||
|
||||
/**
|
||||
* Start to perform actions.
|
||||
*/
|
||||
LockScreenMediaPlaybackActions.prototype.start =
|
||||
function(client) {
|
||||
this.Ensure = require('./ensure.js');
|
||||
this.LockScreen = require('./lockscreen.js');
|
||||
this.FakeMusic = require('./media_playback_fake_music.js');
|
||||
this.client = client;
|
||||
this.musicAppInfo = new this.FakeMusic();
|
||||
this.lockscreen = (new this.LockScreen()).start(client);
|
||||
this.lockscreen.relock();
|
||||
return this;
|
||||
};
|
||||
|
||||
LockScreenMediaPlaybackActions.prototype.ensure =
|
||||
function() {
|
||||
var ensure = (new this.Ensure()).start(this.client);
|
||||
return ensure;
|
||||
};
|
||||
|
||||
LockScreenMediaPlaybackActions.prototype.openMusicApp =
|
||||
function() {
|
||||
this.ensure().launch(this.musicAppInfo.origin);
|
||||
return this;
|
||||
};
|
||||
|
||||
LockScreenMediaPlaybackActions.prototype.switchToMusicApp =
|
||||
function() {
|
||||
this.ensure()
|
||||
.frame(this.musicAppInfo.origin);
|
||||
return this;
|
||||
};
|
||||
|
||||
LockScreenMediaPlaybackActions.prototype.playAlbumOne =
|
||||
function() {
|
||||
this.ensure()
|
||||
.frame(this.musicAppInfo.origin)
|
||||
.element(this.client.findElement(
|
||||
this.musicAppInfo.selector.albumOneElement))
|
||||
.displayed()
|
||||
.actions()
|
||||
.tap()
|
||||
.perform();
|
||||
return this;
|
||||
};
|
||||
|
||||
LockScreenMediaPlaybackActions.prototype.stopPlay =
|
||||
function() {
|
||||
this.ensure()
|
||||
.frame(this.musicAppInfo.origin)
|
||||
.element(this.client.findElement(
|
||||
this.musicAppInfo.selector.stopElement))
|
||||
.displayed()
|
||||
.actions()
|
||||
.tap()
|
||||
.perform();
|
||||
return this;
|
||||
};
|
||||
|
||||
LockScreenMediaPlaybackActions.prototype.togglePausePlay =
|
||||
function() {
|
||||
this.ensure()
|
||||
.frame(this.musicAppInfo.origin)
|
||||
.element(this.client.findElement(
|
||||
this.musicAppInfo.selector.playPauseElement))
|
||||
.displayed()
|
||||
.actions()
|
||||
.tap()
|
||||
.perform();
|
||||
return this;
|
||||
};
|
||||
|
||||
LockScreenMediaPlaybackActions.prototype.killMusicApp =
|
||||
function() {
|
||||
try {
|
||||
this.ensure()
|
||||
.close(this.musicAppInfo.origin);
|
||||
} catch(e) {}
|
||||
return this;
|
||||
};
|
||||
|
||||
LockScreenMediaPlaybackActions.prototype.interruptMusic =
|
||||
function() {
|
||||
this.ensure()
|
||||
.frame(this.musicAppInfo.origin)
|
||||
.element(this.client.findElement(
|
||||
this.musicAppInfo.selector.interruptElement))
|
||||
.displayed()
|
||||
.actions()
|
||||
.tap()
|
||||
.perform();
|
||||
return this;
|
||||
};
|
||||
|
||||
LockScreenMediaPlaybackActions.prototype.lockScreen =
|
||||
function() {
|
||||
this.lockscreen.lock();
|
||||
return this;
|
||||
};
|
||||
|
||||
LockScreenMediaPlaybackActions.prototype.unlockScreen =
|
||||
function() {
|
||||
this.lockscreen.unlock();
|
||||
return this;
|
||||
};
|
||||
|
||||
module.exports = LockScreenMediaPlaybackActions;
|
||||
})(module);
|
||||
75
gaia/apps/system/test/marionette/lib/lockscreen_media_playback_checks.js
Executable file
75
gaia/apps/system/test/marionette/lib/lockscreen_media_playback_checks.js
Executable file
|
|
@ -0,0 +1,75 @@
|
|||
'use strict';
|
||||
|
||||
(function(module) {
|
||||
var LockScreenMediaPlayChecks = function() {
|
||||
this.Ensure = require('./ensure.js');
|
||||
this.FakeMusic = require('./media_playback_fake_music.js');
|
||||
this.assert = require('assert');
|
||||
this.selector = {
|
||||
// should use it in the frame
|
||||
lockScreenContainer: '#lockscreen-media-container',
|
||||
trackElement: '#lockscreen-media-container .track',
|
||||
playPauseElement: '#lockscreen-media-container .play-pause',
|
||||
};
|
||||
this.fakeMusicInfo = new this.FakeMusic();
|
||||
// XXX: when it becomes an app, should has its own app info class.
|
||||
this.lockScreenFrameOrigin = 'app://lockscreen.gaiamobile.org';
|
||||
};
|
||||
|
||||
LockScreenMediaPlayChecks.prototype.start =
|
||||
function (client) {
|
||||
this.client = client;
|
||||
return this;
|
||||
};
|
||||
|
||||
LockScreenMediaPlayChecks.prototype.ensure =
|
||||
function (condition, name) {
|
||||
var ensure = (new this.Ensure()).start(this.client);
|
||||
return ensure;
|
||||
};
|
||||
|
||||
LockScreenMediaPlayChecks.prototype.containerShown =
|
||||
function (requireDisplayed) {
|
||||
var displayed = this.ensure()
|
||||
.frame(this.lockScreenFrameOrigin)
|
||||
.element(this.client.findElement(
|
||||
this.selector.lockScreenContainer))
|
||||
.elements.current.displayed();
|
||||
|
||||
this.assert.equal(displayed, requireDisplayed,
|
||||
'the container.displayed is NOT ' + requireDisplayed);
|
||||
return this;
|
||||
};
|
||||
|
||||
LockScreenMediaPlayChecks.prototype.nowPlayingText =
|
||||
function (artist, title) {
|
||||
var selected =
|
||||
this.ensure()
|
||||
.frame(this.lockScreenFrameOrigin)
|
||||
.element(this.client.findElement(
|
||||
this.selector.trackElement))
|
||||
.as('track')
|
||||
.elements;
|
||||
this.assert.equal(artist + ' — ' + title,
|
||||
selected.track.text(),
|
||||
'the track doesn\'t match the expected one: ' +
|
||||
artist + ' — ' + title +
|
||||
' vs.' +
|
||||
selected.track.text());
|
||||
};
|
||||
|
||||
LockScreenMediaPlayChecks.prototype.isPlaying =
|
||||
function (should) {
|
||||
var button = this.ensure()
|
||||
.frame(this.lockScreenFrameOrigin)
|
||||
.element(this.client.findElement(this.selector.playPauseElement))
|
||||
.elements.current;
|
||||
if (true === should) {
|
||||
return button.getAttribute('data-icon') === 'pause';
|
||||
} else if (false === should) {
|
||||
return button.getAttribute('data-icon') !== 'pause';
|
||||
}
|
||||
};
|
||||
|
||||
module.exports = LockScreenMediaPlayChecks;
|
||||
})(module);
|
||||
91
gaia/apps/system/test/marionette/lib/lockscreen_notification_actions.js
Executable file
91
gaia/apps/system/test/marionette/lib/lockscreen_notification_actions.js
Executable file
|
|
@ -0,0 +1,91 @@
|
|||
/* globals Notification */
|
||||
'use strict';
|
||||
(function (module) {
|
||||
|
||||
var LockScreenNotificationActions = function () {};
|
||||
|
||||
/**
|
||||
* Start to perform actions.
|
||||
*/
|
||||
LockScreenNotificationActions.prototype.start =
|
||||
function (client) {
|
||||
this.actions = client.loader.getActions();
|
||||
this.Ensure = require('./ensure.js');
|
||||
this.Selector = require('./lockscreen_notification_selector.js');
|
||||
this.LockScreen = require('./lockscreen.js');
|
||||
this.client = client;
|
||||
this.created = [];
|
||||
this.lockscreen = (new this.LockScreen()).start(client);
|
||||
this.lockscreen.relock();
|
||||
return this;
|
||||
};
|
||||
|
||||
LockScreenNotificationActions.prototype.ensure =
|
||||
function (condition, name) {
|
||||
var ensure = (new this.Ensure()).start(this.client);
|
||||
return ensure;
|
||||
};
|
||||
|
||||
LockScreenNotificationActions.prototype.query =
|
||||
function () {
|
||||
var selector = (new this.Selector()).start(this.client).fetch();
|
||||
return selector;
|
||||
};
|
||||
|
||||
LockScreenNotificationActions.prototype.fireNotification =
|
||||
function (details) {
|
||||
var notification = this.client.executeScript(
|
||||
function (_details) {
|
||||
var details = {
|
||||
tag: _details.tag,
|
||||
body: _details.body
|
||||
};
|
||||
if (_details.dir) {
|
||||
details.dir = _details.dir;
|
||||
}
|
||||
if (_details.lang) {
|
||||
details.lang = _details.lang;
|
||||
}
|
||||
var notification = new Notification(_details.title, details);
|
||||
// XXX: Because to return DOM notification would block process.
|
||||
return JSON.parse(JSON.stringify(notification));
|
||||
}, [details]);
|
||||
this.created.push(notification);
|
||||
return this;
|
||||
};
|
||||
|
||||
LockScreenNotificationActions.prototype.closeNotification =
|
||||
function (details) {
|
||||
this.query().close(details.tag);
|
||||
return this;
|
||||
};
|
||||
|
||||
LockScreenNotificationActions.prototype.compareNotificationDetails =
|
||||
function (detailsA, detailsB) {
|
||||
if (detailsA.title && detailsB.title !== detailsA.title) {
|
||||
return false;
|
||||
}
|
||||
if (detailsA.body && detailsB.body !== detailsA.body) {
|
||||
return false;
|
||||
}
|
||||
if (detailsA.lang && detailsB.lang !== detailsA.lang) {
|
||||
return false;
|
||||
}
|
||||
if (detailsA.dir && detailsB.dir !== detailsA.dir) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
};
|
||||
|
||||
LockScreenNotificationActions.prototype.lockScreen = function () {
|
||||
this.lockscreen.lock();
|
||||
return this;
|
||||
};
|
||||
|
||||
LockScreenNotificationActions.prototype.unlockScreen = function () {
|
||||
this.lockscreen.unlock();
|
||||
return this;
|
||||
};
|
||||
|
||||
module.exports = LockScreenNotificationActions;
|
||||
})(module);
|
||||
51
gaia/apps/system/test/marionette/lib/lockscreen_notification_checks.js
Executable file
51
gaia/apps/system/test/marionette/lib/lockscreen_notification_checks.js
Executable file
|
|
@ -0,0 +1,51 @@
|
|||
'use strict';
|
||||
|
||||
(function(module) {
|
||||
var LockScreenNotificationChecks = function() {
|
||||
this.Ensure = require('./ensure.js');
|
||||
this.Selector = require('./lockscreen_notification_selector.js');
|
||||
this.assert = require('assert');
|
||||
this.inverseMode = false;
|
||||
this.selector = {
|
||||
// should use it in the frame
|
||||
notifications: '#notifications-lockscreen-container .notification'
|
||||
};
|
||||
// XXX: when it becomes an app, should has its own app info class.
|
||||
this.lockScreenFrameOrigin = 'app://lockscreen.gaiamobile.org';
|
||||
};
|
||||
|
||||
LockScreenNotificationChecks.prototype.start =
|
||||
function (client) {
|
||||
this.client = client;
|
||||
return this;
|
||||
};
|
||||
|
||||
LockScreenNotificationChecks.prototype.ensure =
|
||||
function (condition, name) {
|
||||
var ensure = (new this.Ensure()).start(this.client);
|
||||
return ensure;
|
||||
};
|
||||
|
||||
LockScreenNotificationChecks.prototype.not = function() {
|
||||
this.inverseMode = true;
|
||||
return this;
|
||||
};
|
||||
|
||||
LockScreenNotificationChecks.prototype.query =
|
||||
function () {
|
||||
var selector = (new this.Selector()).start(this.client).fetch();
|
||||
return selector;
|
||||
};
|
||||
|
||||
LockScreenNotificationChecks.prototype.contains = function(details) {
|
||||
// Since in LockScreen, notification may wait a time to remove the
|
||||
// notification, especially when there is a actionable one.
|
||||
var expected = this.inverseMode ? false : true;
|
||||
this.client.waitFor((function() {
|
||||
return this.query().contains(details) === expected;
|
||||
}).bind(this));
|
||||
return this;
|
||||
};
|
||||
|
||||
module.exports = LockScreenNotificationChecks;
|
||||
})(module);
|
||||
132
gaia/apps/system/test/marionette/lib/lockscreen_notification_selector.js
Executable file
132
gaia/apps/system/test/marionette/lib/lockscreen_notification_selector.js
Executable file
|
|
@ -0,0 +1,132 @@
|
|||
'use strict';
|
||||
|
||||
(function (module) {
|
||||
var LockScreenNotificationSelector = function () {
|
||||
this.Ensure = require('./ensure.js');
|
||||
this.assert = require('assert');
|
||||
this.inverseMode = false;
|
||||
this.selector = {
|
||||
// should use it in the frame
|
||||
notifications: '#notifications-lockscreen-container .notification'
|
||||
};
|
||||
// XXX: when it becomes an app, should has its own app info class.
|
||||
this.lockScreenFrameOrigin = 'app://lockscreen.gaiamobile.org';
|
||||
this.notifications = [];
|
||||
};
|
||||
|
||||
LockScreenNotificationSelector.prototype.start =
|
||||
function (client) {
|
||||
// XXX: Remove the low searchTimeout when https://bugzil.la/1141519 lands.
|
||||
// Currently, Marionette can hang during findElements() due to
|
||||
// uncaught exceptions in other contexts; this works around that.
|
||||
this.client = client.scope({ searchTimeout: 2000 });
|
||||
return this;
|
||||
};
|
||||
|
||||
LockScreenNotificationSelector.prototype.ensure =
|
||||
function (condition, name) {
|
||||
var ensure = (new this.Ensure()).start(this.client);
|
||||
return ensure;
|
||||
};
|
||||
|
||||
LockScreenNotificationSelector.prototype.fetch =
|
||||
function () {
|
||||
this.ensure()
|
||||
.frame(this.lockScreenFrameOrigin);
|
||||
this.notifications = this.client.findElements(this.selector.notifications);
|
||||
return this;
|
||||
};
|
||||
|
||||
LockScreenNotificationSelector.prototype.contains =
|
||||
function (detail) {
|
||||
return 0 !== this.notifications
|
||||
.filter((function(element) {
|
||||
return this.ensure().validateElement(element);
|
||||
}).bind(this))
|
||||
.filter((function (element) {
|
||||
// We wrap this in a try/catch in case this is called after a .not().
|
||||
// Otherwise if the element does not exist in the DOM after being
|
||||
// removed it may throw a stale element exception.
|
||||
try {
|
||||
return this.compareNotificationDetails(detail,
|
||||
this.toDetail(element));
|
||||
} catch (e) {
|
||||
return false;
|
||||
}
|
||||
}).bind(this)).length;
|
||||
};
|
||||
|
||||
LockScreenNotificationSelector.prototype.select =
|
||||
function (detail) {
|
||||
return this.notifications
|
||||
.filter((function(element) {
|
||||
return this.ensure().validateElement(element);
|
||||
}).bind(this))
|
||||
.filter((function (element) {
|
||||
return this.compareNotificationDetails(detail, this.toDetail(element));
|
||||
}).bind(this))[0];
|
||||
};
|
||||
|
||||
// Async method. We can't call the method from Marionette side.
|
||||
LockScreenNotificationSelector.prototype.close =
|
||||
function (tag) {
|
||||
if (!tag) {
|
||||
throw new Error('Need a valid tag to get the notification to close');
|
||||
}
|
||||
this.client.executeAsyncScript(function(tag) {
|
||||
window.Notification.get({tag: tag})
|
||||
.then(
|
||||
function(nt) {
|
||||
if (nt.length === 1) {
|
||||
nt[0].close();
|
||||
marionetteScriptFinished(true);
|
||||
return;
|
||||
}
|
||||
marionetteScriptFinished(false);
|
||||
},
|
||||
function(nt) {
|
||||
marionetteScriptFinished(false);
|
||||
});
|
||||
}, [tag]);
|
||||
};
|
||||
|
||||
/**
|
||||
* From MarionetteJS element to detail object.
|
||||
*/
|
||||
LockScreenNotificationSelector.prototype.toDetail =
|
||||
function (element) {
|
||||
var detail = element.scriptWith(function (node) {
|
||||
var title = node.querySelector('.title-container .title');
|
||||
return {
|
||||
title: title.innerHTML,
|
||||
body: node.querySelector('.detail .detail-content').innerHTML,
|
||||
lang: node.getAttribute('lang'),
|
||||
dir: title.dir,
|
||||
manifestURL: node.getAttribute('data-manifest-u-r-l')
|
||||
};
|
||||
});
|
||||
return detail;
|
||||
};
|
||||
|
||||
LockScreenNotificationSelector.prototype.compareNotificationDetails =
|
||||
function (detailsA, detailsB) {
|
||||
if (!detailsA || !detailsB) {
|
||||
throw new Error('No comparable things');
|
||||
}
|
||||
if (detailsA.title && detailsB.title !== detailsA.title) {
|
||||
return false;
|
||||
}
|
||||
if (detailsA.body && detailsB.body !== detailsA.body) {
|
||||
return false;
|
||||
}
|
||||
if (detailsA.lang && detailsB.lang !== detailsA.lang) {
|
||||
return false;
|
||||
}
|
||||
if (detailsA.dir && detailsB.dir !== detailsA.dir) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
};
|
||||
|
||||
module.exports = LockScreenNotificationSelector;
|
||||
})(module);
|
||||
87
gaia/apps/system/test/marionette/lib/lockscreen_passcode_unlock_actions.js
Executable file
87
gaia/apps/system/test/marionette/lib/lockscreen_passcode_unlock_actions.js
Executable file
|
|
@ -0,0 +1,87 @@
|
|||
'use strict';
|
||||
|
||||
var Promise = require('es6-promise').Promise; // jshint ignore:line
|
||||
|
||||
(function(module) {
|
||||
|
||||
var LockScreenPasscodeUnlockActions = function() {};
|
||||
|
||||
/**
|
||||
* Start to perform actions.
|
||||
*/
|
||||
LockScreenPasscodeUnlockActions.prototype.start =
|
||||
function(client) {
|
||||
this.Ensure = require('./ensure.js');
|
||||
this.LockScreen = require('./lockscreen.js');
|
||||
this.client = client;
|
||||
this.lockscreen = (new this.LockScreen()).start(client);
|
||||
this.lockscreen.relock();
|
||||
return this;
|
||||
};
|
||||
|
||||
LockScreenPasscodeUnlockActions.prototype.ensure =
|
||||
function() {
|
||||
var ensure = (new this.Ensure()).start(this.client);
|
||||
return ensure;
|
||||
};
|
||||
|
||||
LockScreenPasscodeUnlockActions.prototype.activateSlidingUnlock =
|
||||
function() {
|
||||
// Due to the failure to drag the slide to right via Marionette,
|
||||
// currently to immediately trigger the event is the only way to
|
||||
// show the result of sliding to unlock.
|
||||
this.client.executeScript(function() {
|
||||
window.wrappedJSObject.dispatchEvent(
|
||||
new CustomEvent('lockscreenslide-activate-right'));
|
||||
});
|
||||
this.client.waitFor((function() {
|
||||
return this.client.executeScript(function() {
|
||||
var screenHeight = window.wrappedJSObject.screen.height;
|
||||
var panel = window.wrappedJSObject.document
|
||||
.querySelector('#lockscreen-panel-passcode');
|
||||
var panelTop = panel.getBoundingClientRect().top;
|
||||
if (screenHeight === panelTop) {
|
||||
return true;
|
||||
} else {
|
||||
return false; // True: only when it rises to 100% fit the screen.
|
||||
}
|
||||
});
|
||||
}).bind(this));
|
||||
};
|
||||
|
||||
LockScreenPasscodeUnlockActions.prototype.pressKey = function(keyChar) {
|
||||
var selector = '#lockscreen-passcode-pad a[data-key="' + keyChar + '"]';
|
||||
// To wait it displayed.
|
||||
this.client.helper.waitForElement(selector).click();
|
||||
};
|
||||
|
||||
LockScreenPasscodeUnlockActions.prototype.waitForUnlock = function() {
|
||||
return new Promise((function(resolve, reject) {
|
||||
this.client.waitFor((function() {
|
||||
var frame = this.client.findElement('#lockscreen-frame');
|
||||
return !frame.displayed();
|
||||
}).bind(this));
|
||||
if (this.client.executeScript(function() {
|
||||
return window.wrappedJSObject.Service.query('locked');
|
||||
})) {
|
||||
reject('Not unlocked before screen off');
|
||||
} else {
|
||||
resolve();
|
||||
}
|
||||
}).bind(this));
|
||||
};
|
||||
|
||||
LockScreenPasscodeUnlockActions.prototype.notUnlock = function() {
|
||||
return new Promise((function(resolve, reject) {
|
||||
if (this.client.executeScript(function() {
|
||||
return window.wrappedJSObject.Service.query('locked');
|
||||
})) {
|
||||
resolve();
|
||||
} else {
|
||||
reject('Unexpected unlocked');
|
||||
}
|
||||
}).bind(this));
|
||||
};
|
||||
|
||||
module.exports = LockScreenPasscodeUnlockActions;
|
||||
})(module);
|
||||
170
gaia/apps/system/test/marionette/lib/media_playback.js
Executable file
170
gaia/apps/system/test/marionette/lib/media_playback.js
Executable file
|
|
@ -0,0 +1,170 @@
|
|||
'use strict';
|
||||
|
||||
var UtilityTray = require('./lib/utility_tray');
|
||||
|
||||
function click(client, element) {
|
||||
// Make sure the element is displayed first. This seems really unnecessary
|
||||
// and is probably masking a bug in Marionette, since all the elements we're
|
||||
// clicking on should be displayed thanks to waitForContainerShown.
|
||||
if (!element.displayed()) {
|
||||
client.waitFor(function() {
|
||||
return element.displayed();
|
||||
});
|
||||
}
|
||||
element.click();
|
||||
}
|
||||
|
||||
var utilityTray;
|
||||
|
||||
function MediaPlaybackContainer(client, container) {
|
||||
this.client = client;
|
||||
this.containerElement = container;
|
||||
utilityTray = new UtilityTray(client);
|
||||
}
|
||||
|
||||
MediaPlaybackContainer.Selector = Object.freeze({
|
||||
nowPlayingElement: '.media-playback-nowplaying',
|
||||
controlsElement: '.media-playback-controls',
|
||||
|
||||
trackElement: '.track',
|
||||
|
||||
previousTrackElement: '.previous',
|
||||
playPauseElement: '.play-pause',
|
||||
nextTrackElement: '.next'
|
||||
});
|
||||
|
||||
MediaPlaybackContainer.prototype = {
|
||||
client: null,
|
||||
container: null,
|
||||
|
||||
get nowPlayingElement() {
|
||||
return this.containerElement.findElement(
|
||||
MediaPlaybackContainer.Selector.nowPlayingElement
|
||||
);
|
||||
},
|
||||
|
||||
get controlsElement() {
|
||||
return this.containerElement.findElement(
|
||||
MediaPlaybackContainer.Selector.controlsElement
|
||||
);
|
||||
},
|
||||
|
||||
get trackElement() {
|
||||
return this.containerElement.findElement(
|
||||
MediaPlaybackContainer.Selector.trackElement
|
||||
);
|
||||
},
|
||||
|
||||
get previousTrackElement() {
|
||||
return this.containerElement.findElement(
|
||||
MediaPlaybackContainer.Selector.previousTrackElement
|
||||
);
|
||||
},
|
||||
|
||||
get playPauseElement() {
|
||||
return this.containerElement.findElement(
|
||||
MediaPlaybackContainer.Selector.playPauseElement
|
||||
);
|
||||
},
|
||||
|
||||
get nextTrackElement() {
|
||||
return this.containerElement.findElement(
|
||||
MediaPlaybackContainer.Selector.nextTrackElement
|
||||
);
|
||||
},
|
||||
|
||||
get trackText() {
|
||||
return this.trackElement.getAttribute('textContent');
|
||||
},
|
||||
|
||||
waitForContainerShown: function(shouldBeShown) {
|
||||
this.client.waitFor(function() {
|
||||
var shown = this.containerElement.displayed();
|
||||
return shown === shouldBeShown;
|
||||
}.bind(this));
|
||||
},
|
||||
|
||||
waitForNowPlayingText: function(artist, title) {
|
||||
this.client.waitFor(function() {
|
||||
return this.trackText === title + ' — ' + artist;
|
||||
}.bind(this));
|
||||
},
|
||||
|
||||
playPause: function() {
|
||||
click(this.client, this.playPauseElement);
|
||||
},
|
||||
|
||||
get isPlaying() {
|
||||
return this.playPauseElement.getAttribute('data-icon') === 'pause';
|
||||
},
|
||||
|
||||
previousTrack: function() {
|
||||
click(this.client, this.previousTrackElement);
|
||||
},
|
||||
|
||||
nextTrack: function() {
|
||||
click(this.client, this.nextTrackElement);
|
||||
}
|
||||
};
|
||||
|
||||
function MediaPlayback(client) {
|
||||
this.client = client;
|
||||
}
|
||||
|
||||
module.exports = MediaPlayback;
|
||||
|
||||
MediaPlayback.Selector = Object.freeze({
|
||||
notificationContainerElement: '#media-playback-container',
|
||||
lockscreenContainerElement: '#lockscreen-media-container'
|
||||
});
|
||||
|
||||
MediaPlayback.prototype = {
|
||||
client: null,
|
||||
|
||||
get notificationContainerElement() {
|
||||
return this.client.findElement(
|
||||
MediaPlayback.Selector.notificationContainerElement
|
||||
);
|
||||
},
|
||||
|
||||
get lockscreenContainerElement() {
|
||||
var lockScreenFrame = this.client.findElement('#lockscreen-frame');
|
||||
if (!lockScreenFrame) {
|
||||
throw new Error('--- no lockscreen frame ---');
|
||||
}
|
||||
this.client.switchToFrame(lockScreenFrame);
|
||||
var container = this.client.findElement(
|
||||
MediaPlayback.Selector.lockscreenContainerElement
|
||||
);
|
||||
this.client.switchToFrame();
|
||||
return container;
|
||||
},
|
||||
|
||||
inUtilityTray: function(callback) {
|
||||
utilityTray.open();
|
||||
callback(new MediaPlaybackContainer(
|
||||
this.client, this.notificationContainerElement
|
||||
));
|
||||
utilityTray.close();
|
||||
},
|
||||
|
||||
lockScreen: function() {
|
||||
this.client.executeScript(function() {
|
||||
window.wrappedJSObject.Service.request('lock', { 'forcibly': true });
|
||||
});
|
||||
},
|
||||
|
||||
unlockScreen: function() {
|
||||
this.client.executeScript(function() {
|
||||
window.wrappedJSObject.Service.request('unlock', { 'forcibly': true });
|
||||
});
|
||||
},
|
||||
|
||||
inLockscreen: function(callback) {
|
||||
this.lockScreen();
|
||||
callback(new MediaPlaybackContainer(
|
||||
this.client, this.lockscreenContainerElement
|
||||
));
|
||||
this.unlockScreen();
|
||||
}
|
||||
};
|
||||
140
gaia/apps/system/test/marionette/lib/media_playback_actions.js
Executable file
140
gaia/apps/system/test/marionette/lib/media_playback_actions.js
Executable file
|
|
@ -0,0 +1,140 @@
|
|||
'use strict';
|
||||
(function(module) {
|
||||
|
||||
var MediaPlaybackActions = function() {};
|
||||
|
||||
/**
|
||||
* Start to perform actions.
|
||||
*/
|
||||
MediaPlaybackActions.prototype.start =
|
||||
function(client) {
|
||||
this.Ensure = require('./ensure.js');
|
||||
this.FakeMusic = require('./media_playback_fake_music.js');
|
||||
this.client = client;
|
||||
this.selector = {
|
||||
trayHandler: '#top-panel',
|
||||
grippy: '#utility-tray-grippy'
|
||||
};
|
||||
this.musicAppInfo = new this.FakeMusic();
|
||||
// Block until System is ready.
|
||||
this.ensure().frame().systemReady();
|
||||
return this;
|
||||
};
|
||||
|
||||
MediaPlaybackActions.prototype.ensure =
|
||||
function(condition, name) {
|
||||
var ensure = (new this.Ensure()).start(this.client);
|
||||
return ensure;
|
||||
};
|
||||
|
||||
MediaPlaybackActions.prototype.pullDownTray =
|
||||
function() {
|
||||
this.ensure()
|
||||
.frame()
|
||||
.element(this.client.helper.waitForElement(this.selector.trayHandler))
|
||||
.actions()
|
||||
.pull(0, 400)
|
||||
.perform()
|
||||
.element(this.client.findElement(this.selector.grippy))
|
||||
.located()
|
||||
.must(function(elements) {
|
||||
return elements.current.location().y > 400;
|
||||
});
|
||||
return this;
|
||||
};
|
||||
|
||||
MediaPlaybackActions.prototype.pullUpTray =
|
||||
function() {
|
||||
this.ensure()
|
||||
.frame()
|
||||
.element(this.client.helper.waitForElement(this.selector.grippy))
|
||||
.must(function(elements) {
|
||||
return elements.current.location().y > 400;
|
||||
})
|
||||
.actions()
|
||||
.pull(0, -400)
|
||||
.perform()
|
||||
.located()
|
||||
.element(this.client.findElement(this.selector.grippy))
|
||||
.must(function(elements) {
|
||||
return elements.current.location().y < 30;
|
||||
});
|
||||
return this;
|
||||
};
|
||||
|
||||
MediaPlaybackActions.prototype.openMusicApp =
|
||||
function() {
|
||||
this.ensure().launch(this.musicAppInfo.origin);
|
||||
return this;
|
||||
};
|
||||
|
||||
MediaPlaybackActions.prototype.switchToMusicApp =
|
||||
function() {
|
||||
this.ensure()
|
||||
.frame(this.musicAppInfo.origin);
|
||||
return this;
|
||||
};
|
||||
|
||||
MediaPlaybackActions.prototype.playAlbumOne =
|
||||
function() {
|
||||
this.ensure()
|
||||
.frame(this.musicAppInfo.origin)
|
||||
.element(this.client.findElement(
|
||||
this.musicAppInfo.selector.albumOneElement))
|
||||
.displayed()
|
||||
.actions()
|
||||
.tap()
|
||||
.perform();
|
||||
return this;
|
||||
};
|
||||
|
||||
MediaPlaybackActions.prototype.stopPlay =
|
||||
function() {
|
||||
this.ensure()
|
||||
.frame(this.musicAppInfo.origin)
|
||||
.element(this.client.findElement(
|
||||
this.musicAppInfo.selector.stopElement))
|
||||
.displayed()
|
||||
.actions()
|
||||
.tap()
|
||||
.perform();
|
||||
return this;
|
||||
};
|
||||
|
||||
MediaPlaybackActions.prototype.togglePausePlay =
|
||||
function() {
|
||||
this.ensure()
|
||||
.frame(this.musicAppInfo.origin)
|
||||
.element(this.client.findElement(
|
||||
this.musicAppInfo.selector.playPauseElement))
|
||||
.displayed()
|
||||
.actions()
|
||||
.tap()
|
||||
.perform();
|
||||
return this;
|
||||
};
|
||||
|
||||
MediaPlaybackActions.prototype.killMusicApp =
|
||||
function() {
|
||||
try {
|
||||
this.ensure()
|
||||
.close(this.musicAppInfo.origin);
|
||||
} catch(e) {}
|
||||
return this;
|
||||
};
|
||||
|
||||
MediaPlaybackActions.prototype.interruptMusic =
|
||||
function() {
|
||||
this.ensure()
|
||||
.frame(this.musicAppInfo.origin)
|
||||
.element(this.client.findElement(
|
||||
this.musicAppInfo.selector.interruptElement))
|
||||
.displayed()
|
||||
.actions()
|
||||
.tap()
|
||||
.perform();
|
||||
return this;
|
||||
};
|
||||
|
||||
module.exports = MediaPlaybackActions;
|
||||
})(module);
|
||||
72
gaia/apps/system/test/marionette/lib/media_playback_checks.js
Executable file
72
gaia/apps/system/test/marionette/lib/media_playback_checks.js
Executable file
|
|
@ -0,0 +1,72 @@
|
|||
'use strict';
|
||||
|
||||
(function(module) {
|
||||
var MediaPlayChecks = function() {
|
||||
this.Ensure = require('./ensure.js');
|
||||
this.FakeMusic = require('./media_playback_fake_music.js');
|
||||
this.assert = require('assert');
|
||||
this.selector = {
|
||||
notificationContainer: '#media-playback-container',
|
||||
trackElement: '#media-playback-container .track',
|
||||
playPauseElement: '#media-playback-container .play-pause'
|
||||
};
|
||||
this.fakeMusicInfo = new this.FakeMusic();
|
||||
};
|
||||
|
||||
MediaPlayChecks.prototype.start =
|
||||
function (client) {
|
||||
this.client = client;
|
||||
return this;
|
||||
};
|
||||
|
||||
MediaPlayChecks.prototype.ensure =
|
||||
function (condition, name) {
|
||||
var ensure = (new this.Ensure()).start(this.client);
|
||||
return ensure;
|
||||
};
|
||||
|
||||
MediaPlayChecks.prototype.containerShown =
|
||||
function (requireDisplayed) {
|
||||
var displayed = this.ensure()
|
||||
.frame()
|
||||
.element(this.client.findElement(
|
||||
this.selector.notificationContainer))
|
||||
.elements.current.displayed();
|
||||
|
||||
this.assert.equal(displayed, requireDisplayed,
|
||||
'the container.displayed is NOT ' + requireDisplayed);
|
||||
return this;
|
||||
};
|
||||
|
||||
MediaPlayChecks.prototype.nowPlayingText =
|
||||
function (artist, title) {
|
||||
var selected =
|
||||
this.ensure()
|
||||
.frame(this.lockScreenFrameOrigin)
|
||||
.element(this.client.findElement(
|
||||
this.selector.trackElement))
|
||||
.as('track')
|
||||
.elements;
|
||||
this.assert.equal(title + ' — ' + artist,
|
||||
selected.track.text(),
|
||||
'the track doesn\'t match the expected one: ' +
|
||||
artist + ' — ' + title +
|
||||
' vs.' +
|
||||
selected.track.text());
|
||||
};
|
||||
|
||||
MediaPlayChecks.prototype.isPlaying =
|
||||
function (should) {
|
||||
var button = this.ensure()
|
||||
.frame(this.lockScreenFrameOrigin)
|
||||
.element(this.client.findElement(this.selector.playPauseElement))
|
||||
.elements.current;
|
||||
if (true === should) {
|
||||
return button.getAttribute('data-icon') === 'pause';
|
||||
} else if (false === should) {
|
||||
return button.getAttribute('data-icon') !== 'pause';
|
||||
}
|
||||
};
|
||||
|
||||
module.exports = MediaPlayChecks;
|
||||
})(module);
|
||||
19
gaia/apps/system/test/marionette/lib/media_playback_fake_music.js
Executable file
19
gaia/apps/system/test/marionette/lib/media_playback_fake_music.js
Executable file
|
|
@ -0,0 +1,19 @@
|
|||
'use strict';
|
||||
(function(module) {
|
||||
var FakeMusic = function() {
|
||||
var appScheme = 'app://';
|
||||
this.selector = {
|
||||
albumOneElement: '#album-one',
|
||||
playPauseElement: '#play-pause',
|
||||
stopElement: '#stop',
|
||||
previousTrackElement: '#previous',
|
||||
nextTrackElement: '#next',
|
||||
interruptElement: '#interrupt'
|
||||
};
|
||||
this.domain = 'fakemusic.gaiamobile.org';
|
||||
this.origin = appScheme + this.domain;
|
||||
this.path = __dirname + '/../../apps/fakemusic';
|
||||
};
|
||||
|
||||
module.exports = FakeMusic;
|
||||
})(module);
|
||||
209
gaia/apps/system/test/marionette/lib/notification.js
Executable file
209
gaia/apps/system/test/marionette/lib/notification.js
Executable file
|
|
@ -0,0 +1,209 @@
|
|||
/* global dump */
|
||||
/* jshint nonew: false */
|
||||
'use strict';
|
||||
|
||||
var Marionette = require('marionette-client');
|
||||
|
||||
function NotificationTest(client, details, delay_create) {
|
||||
this.client = client;
|
||||
this.actions = new Marionette.Actions(client);
|
||||
this.tag = details.tag;
|
||||
this.title = details.title;
|
||||
this.body = details.body;
|
||||
this.dir = details.dir;
|
||||
this.lang = details.lang;
|
||||
if (delay_create !== true) {
|
||||
this.create();
|
||||
}
|
||||
}
|
||||
|
||||
NotificationTest.prototype = {
|
||||
Selectors: {
|
||||
toaster: '#notification-toaster'
|
||||
},
|
||||
client: null,
|
||||
tag: null,
|
||||
close: function() {
|
||||
var ret;
|
||||
this.client.executeAsyncScript(function(notifyTag) {
|
||||
var n = Notification.get({tag: notifyTag});
|
||||
n.then(
|
||||
function(nt) {
|
||||
if (nt.length === 1) {
|
||||
nt[0].close();
|
||||
marionetteScriptFinished(true);
|
||||
return;
|
||||
}
|
||||
marionetteScriptFinished(false);
|
||||
},
|
||||
function(nt) {
|
||||
marionetteScriptFinished(false);
|
||||
});
|
||||
}, [this.tag], function(err, value) {
|
||||
ret = value;
|
||||
});
|
||||
|
||||
return ret;
|
||||
},
|
||||
dumpContainer: function() {
|
||||
this.client.executeScript(function() {
|
||||
dump(
|
||||
document.getElementById('desktop-notifications-container').innerHTML);
|
||||
});
|
||||
},
|
||||
create: function() {
|
||||
this.client.executeScript(function(notifyTag, notifyTitle, notifyBody,
|
||||
notifyDir, notifyLang) {
|
||||
var details = { tag: notifyTag,
|
||||
body: notifyBody};
|
||||
if (notifyDir) {
|
||||
details.dir = notifyDir;
|
||||
}
|
||||
if (notifyLang) {
|
||||
details.lang = notifyLang;
|
||||
}
|
||||
|
||||
new Notification(notifyTitle, details);
|
||||
}, [this.tag, this.title, this.body, this.dir, this.lang]);
|
||||
}
|
||||
};
|
||||
|
||||
function NotificationList(client) {
|
||||
this.client = client;
|
||||
this.selectors = NotificationList.Selector;
|
||||
this.notifications = null;
|
||||
}
|
||||
|
||||
NotificationList.Selector = Object.freeze((function() {
|
||||
var listSelector = '#desktop-notifications-container';
|
||||
var itemsSelector = listSelector + ' .notification';
|
||||
var allItemsSelector = '#notifications-container .notification';
|
||||
var countSelector = '#notification-some';
|
||||
var systemUpdateSelector = '#update-manager-container .title-container';
|
||||
return {
|
||||
items: itemsSelector,
|
||||
allItems: allItemsSelector,
|
||||
systemUpdate: systemUpdateSelector,
|
||||
notificationsCount: countSelector
|
||||
};
|
||||
})());
|
||||
|
||||
NotificationList.prototype = {
|
||||
|
||||
_remoteGetNotificationDetails: function(selector) {
|
||||
var nodes = document.querySelectorAll(selector);
|
||||
var details = [];
|
||||
/* jshint -W084 */
|
||||
for (var node, i = 0; node = nodes[i]; i++) {
|
||||
var id = node.getAttribute('data-notification-id');
|
||||
var query = selector + '[data-notification-id="' + id + '"]';
|
||||
details.push({
|
||||
title: document.querySelector(query + ' > .title-container .title')
|
||||
.innerHTML,
|
||||
body: document.querySelector(query + ' > .detail .detail-content')
|
||||
.innerHTML,
|
||||
lang: document.querySelector(query).getAttribute('lang'),
|
||||
dir: document.querySelector(query).querySelector('.title').dir,
|
||||
manifestURL: node.getAttribute('data-manifest-u-r-l'),
|
||||
query: query
|
||||
});
|
||||
}
|
||||
return details;
|
||||
},
|
||||
|
||||
// fetch the list of open notifications from system tray
|
||||
refresh: function() {
|
||||
this.notifications = this.client.executeScript(
|
||||
this._remoteGetNotificationDetails,
|
||||
[this.selectors.items]);
|
||||
},
|
||||
|
||||
// return a list of notifications for a certain app
|
||||
getForApp: function(manifestURL) {
|
||||
if (!this.notifications) {
|
||||
return [];
|
||||
}
|
||||
return this.notifications.filter(function(notification) {
|
||||
return notification.manifestURL === manifestURL;
|
||||
});
|
||||
},
|
||||
|
||||
// get a count of notifications with a certain title and body
|
||||
getCount: function(details) {
|
||||
var list;
|
||||
if (details.manifestURL) {
|
||||
list = this.getForApp(details.manifestURL);
|
||||
} else {
|
||||
list = this.notifications;
|
||||
}
|
||||
var count = 0;
|
||||
for (var i = 0; list && i < list.length; i++) {
|
||||
var notification = list[i];
|
||||
if (details.title && notification.title !== details.title) {
|
||||
continue;
|
||||
}
|
||||
if (details.body && notification.body !== details.body) {
|
||||
continue;
|
||||
}
|
||||
if (details.lang && notification.lang !== details.lang) {
|
||||
continue;
|
||||
}
|
||||
if (details.dir && notification.dir !== details.dir) {
|
||||
continue;
|
||||
}
|
||||
++count;
|
||||
}
|
||||
return count;
|
||||
},
|
||||
|
||||
// perform a tap action on the notification list
|
||||
tap: function(notificationDetails) {
|
||||
this.client.helper.waitForElement(notificationDetails.query).tap(1, 1);
|
||||
},
|
||||
|
||||
// make sure we have an item with given title and body
|
||||
contains: function(details, shouldNot) {
|
||||
this.client.waitFor((function() {
|
||||
var count = this.getCount(details);
|
||||
if (shouldNot) {
|
||||
return 0 === count;
|
||||
} else {
|
||||
return 0 < count;
|
||||
}
|
||||
}).bind(this));
|
||||
return true;
|
||||
},
|
||||
|
||||
waitForNotificationCount: function(expected) {
|
||||
var count;
|
||||
this.client.waitFor(function() {
|
||||
count = this.client.executeScript(
|
||||
this._getNotificationsCountText,
|
||||
[this.selectors.notificationsCount]);
|
||||
return expected === count;
|
||||
}.bind(this));
|
||||
return true;
|
||||
},
|
||||
|
||||
clear: function() {
|
||||
this.client.executeAsyncScript(function() {
|
||||
var clearAll = document.getElementById('notification-clear');
|
||||
var event = document.createEvent('CustomEvent');
|
||||
event.initCustomEvent('click', true, true, {});
|
||||
clearAll.dispatchEvent(event);
|
||||
marionetteScriptFinished(true);
|
||||
});
|
||||
this.waitForNotificationCount(0);
|
||||
},
|
||||
|
||||
_getNotificationsCountText: function(selector) {
|
||||
var element = document.querySelector(selector);
|
||||
var args = JSON.parse(element.dataset.l10nArgs);
|
||||
return parseInt(args.n);
|
||||
}
|
||||
};
|
||||
|
||||
module.exports = {
|
||||
NotificationTest: NotificationTest,
|
||||
NotificationList: NotificationList
|
||||
};
|
||||
126
gaia/apps/system/test/marionette/lib/pinning_the_web.js
Executable file
126
gaia/apps/system/test/marionette/lib/pinning_the_web.js
Executable file
|
|
@ -0,0 +1,126 @@
|
|||
'use strict';
|
||||
/* global module */
|
||||
|
||||
var Rocketbar = require('./rocketbar');
|
||||
|
||||
function PinningTheWeb(client) {
|
||||
this.client = client;
|
||||
this.rocketbar = new Rocketbar(client);
|
||||
this.system = client.loader.getAppClass('system');
|
||||
}
|
||||
|
||||
|
||||
PinningTheWeb.prototype = {
|
||||
Selectors: {
|
||||
pinDialog: '#pin-page-dialog',
|
||||
pinPageButton: '#pin-page-container gaia-button',
|
||||
pinSiteButton: '#pin-site-container gaia-button'
|
||||
},
|
||||
|
||||
get pinDialog() {
|
||||
return this.client.helper.waitForElement(this.Selectors.pinDialog, {
|
||||
timeout: 90000
|
||||
});
|
||||
},
|
||||
|
||||
get pinPageButton() {
|
||||
return this.client.helper.waitForElement(this.Selectors.pinPageButton);
|
||||
},
|
||||
|
||||
get pinSiteButton() {
|
||||
return this.client.helper.waitForElement(this.Selectors.pinSiteButton);
|
||||
},
|
||||
|
||||
_openUrl: function openUrl(url) {
|
||||
this.client.switchToFrame();
|
||||
this.rocketbar.appTitleFocus();
|
||||
this.rocketbar.enterText(url, true);
|
||||
this.rocketbar.switchToBrowserFrame(url);
|
||||
this.client.switchToFrame();
|
||||
},
|
||||
|
||||
// Open a URL, tap on the site icon and tap the pin site button.
|
||||
openAndPinSite: function openAndPinSite(url) {
|
||||
this.client.switchToFrame();
|
||||
this.rocketbar.appTitleFocus();
|
||||
this.rocketbar.enterText(url, true);
|
||||
this.system.gotoBrowser(url);
|
||||
this.client.scope({ searchTimeout: 100 }).waitFor(function() {
|
||||
this.client.switchToFrame();
|
||||
try {
|
||||
this.system.siteIcon.tap();
|
||||
} catch (e) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}.bind(this));
|
||||
this.system.pinButton.tap();
|
||||
this.client.waitFor(function() {
|
||||
var toast = this.client.findElement('#screen > gaia-toast');
|
||||
return toast && toast.displayed();
|
||||
}.bind(this));
|
||||
},
|
||||
|
||||
// Open Pin Dialog for URL.
|
||||
openPinDialog: function(url) {
|
||||
this._openUrl(url);
|
||||
this._clickPinContextMenu();
|
||||
this.client.helper.waitForElement(this.Selectors.pinDialog);
|
||||
},
|
||||
|
||||
// Open a URL, open the pin dialog and tap the pin site button.
|
||||
openAndPinSiteFromBrowser: function (url) {
|
||||
this._openUrl(url);
|
||||
this._clickPinContextMenu();
|
||||
var pinDialog = this.pinDialog;
|
||||
// When running tests in mulet locally, part of the pin site button
|
||||
// is cut off at the bottom of the screen. To work around this we
|
||||
// tap the pin button in the upper left hand corner.
|
||||
this.pinSiteButton.click();
|
||||
this.client.helper.waitForElementToDisappear(pinDialog);
|
||||
},
|
||||
|
||||
// Open a URL, open the pin dialog and tap the pin/unpin page button.
|
||||
openAndPinPage: function openAndPinPage(url) {
|
||||
this._openUrl(url);
|
||||
this._clickPinContextMenu();
|
||||
this.pinPageButton.click();
|
||||
this.client.helper.waitForElementToDisappear(this.pinDialog);
|
||||
},
|
||||
|
||||
chromeIsPinned: function chromeIsPinned() {
|
||||
var classes = this.system.appChrome.getAttribute('class');
|
||||
var notScrollable = classes.indexOf('collapsible') < 0;
|
||||
|
||||
return this.chromeisMinimized() && notScrollable;
|
||||
},
|
||||
|
||||
chromeisMinimized: function chromeisMinimized() {
|
||||
var classes = this.system.appChrome.getAttribute('class');
|
||||
var isMinimized = classes.indexOf('maximized') < 0;
|
||||
|
||||
return isMinimized;
|
||||
},
|
||||
|
||||
_unpinChrome: function() {
|
||||
this.client.waitFor(function() {
|
||||
// Tap to expand the browser chrome.
|
||||
this.system.appUrlbar.tap();
|
||||
return !this.chromeisMinimized();
|
||||
}.bind(this));
|
||||
},
|
||||
|
||||
_clickPinContextMenu: function() {
|
||||
this.client.switchToFrame();
|
||||
if (this.chromeisMinimized()) {
|
||||
this._unpinChrome();
|
||||
}
|
||||
this.system.appChromeContextLink.tap();
|
||||
var menu = this.system.appChromeContextMenu;
|
||||
this.system.appChromeContextMenuPin.tap();
|
||||
this.client.helper.waitForElementToDisappear(menu);
|
||||
this.client.helper.waitForElement(this.pinDialog);
|
||||
}
|
||||
};
|
||||
|
||||
module.exports = PinningTheWeb;
|
||||
207
gaia/apps/system/test/marionette/lib/rocketbar.js
Executable file
207
gaia/apps/system/test/marionette/lib/rocketbar.js
Executable file
|
|
@ -0,0 +1,207 @@
|
|||
'use strict';
|
||||
/* global module */
|
||||
|
||||
/**
|
||||
* A Marionette test harness for Rocketbar.
|
||||
*/
|
||||
|
||||
var Marionette = require('marionette-client');
|
||||
|
||||
/**
|
||||
* @constructor
|
||||
* @param {Marionette.Client} client Marionette client to use.
|
||||
*/
|
||||
function Rocketbar(client) {
|
||||
this.client = client.scope({ searchTimeout: 200000 });
|
||||
this.actions = new Marionette.Actions(client);
|
||||
this.system = client.loader.getAppClass('system');
|
||||
}
|
||||
|
||||
module.exports = Rocketbar;
|
||||
|
||||
Rocketbar.prototype = {
|
||||
selectors: {
|
||||
activeBrowserFrame: '.browser.appWindow.active',
|
||||
screen: '#screen',
|
||||
rocketbar: '#rocketbar',
|
||||
title: '#rocketbar-title',
|
||||
input: '#rocketbar-input',
|
||||
cancel: '#rocketbar-cancel',
|
||||
clear: '#rocketbar-clear',
|
||||
backdrop: '#rocketbar-backdrop',
|
||||
results: '#rocketbar-results',
|
||||
appTitle: '.appWindow.active .chrome .title'
|
||||
},
|
||||
|
||||
/**
|
||||
* Tap the Rocketbar to show and focus input field.
|
||||
*/
|
||||
focus: function() {
|
||||
var rocketbar = this.client.findElement(this.selectors.rocketbar);
|
||||
this.client.waitFor(rocketbar.displayed.bind(rocketbar));
|
||||
|
||||
// Poll the page to ensure rocketbar is enabled before tapping on it.
|
||||
var lastVal = false;
|
||||
this.client.waitFor(function() {
|
||||
this.client.executeScript(function() {
|
||||
var win = window.wrappedJSObject;
|
||||
return win.Service.query('Rocketbar.enabled');
|
||||
}, function(err, value) {
|
||||
lastVal = value;
|
||||
});
|
||||
return lastVal;
|
||||
}.bind(this));
|
||||
|
||||
rocketbar.tap();
|
||||
var input =
|
||||
this.client.findElement(this.selectors.input);
|
||||
this.client.waitFor(input.displayed.bind(input));
|
||||
},
|
||||
|
||||
/**
|
||||
* Focuses the rocketbar from the homescreen.
|
||||
* This is a temporary method while the homescreen search trigger lives in
|
||||
* the homescreen app. If we move it to the system app we can remove this.
|
||||
*/
|
||||
homescreenFocus: function() {
|
||||
// Only verticalhome has a search bar built in, so check to see if the
|
||||
// app one is visible first and use that.
|
||||
var title;
|
||||
this.client.switchToFrame();
|
||||
try {
|
||||
title = this.client.scope({ searchTimeout: 100 }).
|
||||
findElement(this.selectors.appTitle);
|
||||
} catch (e) { }
|
||||
if (title && title.displayed()) {
|
||||
title.tap();
|
||||
this.client.switchToFrame();
|
||||
return;
|
||||
}
|
||||
|
||||
this.client.switchToFrame();
|
||||
var homeLib = this.client.loader.getAppClass('verticalhome');
|
||||
homeLib.waitForLaunch();
|
||||
homeLib.focusRocketBar();
|
||||
},
|
||||
|
||||
performSearchInBrowser: function(text) {
|
||||
this.enterText(text, true);
|
||||
this.waitForBrowserFrame();
|
||||
},
|
||||
|
||||
/**
|
||||
* Trigger rocketbar from app title.
|
||||
*/
|
||||
appTitleFocus: function() {
|
||||
var title = this.client.helper.waitForElement(this.selectors.appTitle);
|
||||
title.click();
|
||||
},
|
||||
|
||||
waitForInputToBecomeVisible: function() {
|
||||
this.client.helper.waitForElement(this.selectors.input);
|
||||
},
|
||||
|
||||
/**
|
||||
* Send keys to the Rocketbar (needs to be focused first).
|
||||
*/
|
||||
enterText: function(text, submit) {
|
||||
var input =
|
||||
this.client.findElement(this.selectors.input);
|
||||
input.clear();
|
||||
this.client.waitFor(input.displayed.bind(input));
|
||||
input.sendKeys(text);
|
||||
|
||||
if (submit) {
|
||||
input.sendKeys('\uE006');
|
||||
}
|
||||
|
||||
if (!text.length) {
|
||||
this.client.executeScript(function() {
|
||||
window.wrappedJSObject.Service.request('Rocketbar:handleInput');
|
||||
});
|
||||
}
|
||||
|
||||
// Manually blur the input with script or the keyboard can mess up
|
||||
// visibility in tests.
|
||||
input.scriptWith(function(el) {
|
||||
el.blur();
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* Switches to a search browser frame.
|
||||
* The URL of a search provider will generally contain '{searchTerms}', so
|
||||
* we replace that with the search text.
|
||||
*/
|
||||
switchToSearchFrame: function(url, text) {
|
||||
url = url.replace('{searchTerms}', '');
|
||||
return this.switchToBrowserFrame(url);
|
||||
},
|
||||
|
||||
/**
|
||||
* Switch to a browser window frame which matches the given URL.
|
||||
*/
|
||||
switchToBrowserFrame: function(url) {
|
||||
url = url.replace(/\s+/g, '%20');
|
||||
var browserFrame = this.client.findElement('iframe[src*="' + url + '"]');
|
||||
this.client.switchToFrame(browserFrame);
|
||||
},
|
||||
|
||||
/**
|
||||
* Wait for Rocketbar to initialise and expand.
|
||||
*/
|
||||
waitForLoad: function() {
|
||||
var element = this.rocketbar;
|
||||
this.client.waitFor(function() {
|
||||
var rocketbarClass = element.getAttribute('class');
|
||||
return rocketbarClass.indexOf('expanded') != -1;
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* Wait for an opened browser frame to complete showing
|
||||
*/
|
||||
waitForBrowserFrame: function() {
|
||||
this.client.switchToFrame();
|
||||
var browser = this.selectors.activeBrowserFrame;
|
||||
this.client.helper.waitForElement(browser);
|
||||
},
|
||||
|
||||
goToURL: function(url) {
|
||||
this.homescreenFocus();
|
||||
this.enterText(url, true);
|
||||
this.system.waitForUrlLoaded(url);
|
||||
},
|
||||
|
||||
get rocketbar() {
|
||||
return this.client.findElement(this.selectors.rocketbar);
|
||||
},
|
||||
|
||||
get results() {
|
||||
return this.client.findElement(this.selectors.results);
|
||||
},
|
||||
|
||||
get screen() {
|
||||
return this.client.findElement(this.selectors.screen);
|
||||
},
|
||||
|
||||
get title() {
|
||||
return this.client.findElement(this.selectors.title);
|
||||
},
|
||||
|
||||
get input() {
|
||||
return this.client.findElement(this.selectors.input);
|
||||
},
|
||||
|
||||
get cancel() {
|
||||
return this.client.findElement(this.selectors.cancel);
|
||||
},
|
||||
|
||||
get clear() {
|
||||
return this.client.findElement(this.selectors.clear);
|
||||
},
|
||||
|
||||
get backdrop() {
|
||||
return this.client.findElement(this.selectors.backdrop);
|
||||
}
|
||||
};
|
||||
186
gaia/apps/system/test/marionette/lib/selection_helper.js
Executable file
186
gaia/apps/system/test/marionette/lib/selection_helper.js
Executable file
|
|
@ -0,0 +1,186 @@
|
|||
'use strict';
|
||||
|
||||
var Actions = require('marionette-client').Actions;
|
||||
function SelectionHelper(client, element) {
|
||||
this.client = client;
|
||||
this.isInputOrTextarea =
|
||||
(element.getAttribute('tagName') === 'INPUT' ||
|
||||
element.getAttribute('tagName') === 'TEXTAREA');
|
||||
this.element = element;
|
||||
this.action = new Actions(this.client);
|
||||
}
|
||||
|
||||
module.exports = SelectionHelper;
|
||||
|
||||
SelectionHelper.prototype = {
|
||||
get content() {
|
||||
if (this.isInputOrTextarea) {
|
||||
return this.element.getAttribute('value');
|
||||
} else {
|
||||
return this.element.text;
|
||||
}
|
||||
},
|
||||
|
||||
get selectedContent() {
|
||||
return this.client.executeScript(this.getSelectionCmd() +
|
||||
'return sel.toString();', [this.element], null, 'system');
|
||||
},
|
||||
|
||||
getSelectionCmd: function() {
|
||||
if (this.isInputOrTextarea) {
|
||||
return 'var sel = arguments[0].editor.selection;';
|
||||
} else {
|
||||
return 'var sel = window.getSelection();';
|
||||
}
|
||||
},
|
||||
|
||||
selectionRectList: function(idx) {
|
||||
var cmd = this.getSelectionCmd() + 'return sel.getRangeAt(' + idx + ')' +
|
||||
'.getClientRects();';
|
||||
return this.client.executeScript(cmd, [this.element], null, 'system');
|
||||
},
|
||||
|
||||
rangeCount: function() {
|
||||
var cmd = this.getSelectionCmd() + 'return sel.rangeCount;';
|
||||
return this.client.executeScript(cmd, [this.element], null, 'system');
|
||||
},
|
||||
|
||||
/**
|
||||
* Move caret by absolute position.
|
||||
*
|
||||
* @param {Object} data
|
||||
* {caretA: {
|
||||
* offset: {
|
||||
* x: //poistion relative to element,
|
||||
* y: //poistion relative to element
|
||||
* },
|
||||
* caretB: {
|
||||
* offset: {
|
||||
* x: //poistion relative to element,
|
||||
* y: //poistion relative to element
|
||||
* }
|
||||
* }}
|
||||
*
|
||||
*/
|
||||
moveCaretByPosition: function(data) {
|
||||
var caretPositions = this.selectionLocationHelper();
|
||||
if (data.caretA) {
|
||||
this.action.flick(this.element, caretPositions.caretA.x,
|
||||
caretPositions.caretA.y + 15,
|
||||
caretPositions.caretA.x + data.caretA.offset.x,
|
||||
caretPositions.caretA.y + 15 + data.caretA.offset.y).perform();
|
||||
this.client.wait(500);
|
||||
}
|
||||
if (data.caretB) {
|
||||
this.action.flick(this.element, caretPositions.caretB.x,
|
||||
caretPositions.caretB.y + 15,
|
||||
caretPositions.caretB.x + data.caretB.offset.x,
|
||||
caretPositions.caretB.y + 15 + data.caretB.offset.y).perform();
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Move caret by word's position.
|
||||
*
|
||||
* @param {Object} data
|
||||
* {caretA: {offset: //integer}, caretB: {offset //integer}}
|
||||
*
|
||||
*/
|
||||
moveCaretByWords: function(data) {
|
||||
if (!this.isInputOrTextarea) {
|
||||
console.log('only support input or textarea');
|
||||
return;
|
||||
}
|
||||
var caretA = data.caretA;
|
||||
var caretB = data.caretB;
|
||||
var caretPositions = this.selectionLocationHelper();
|
||||
var content = this.content;
|
||||
var contentLength = content.length;
|
||||
|
||||
this.client.executeScript('arguments[0].setSelectionRange(' +
|
||||
(caretA ? caretA.offset : 0) + ',' +
|
||||
(contentLength + (caretB ? caretB.offset : 0)) + ' )',
|
||||
[this.element]);
|
||||
|
||||
var newCaretPositions = this.selectionLocationHelper();
|
||||
|
||||
var eltSize = this.element.size();
|
||||
this.action.tap(this.element, eltSize.width / 2, eltSize.height / 2)
|
||||
.wait(2).press(this.element, eltSize.width / 2, eltSize.height / 2).
|
||||
moveByOffset(0, 0).wait(2).release().perform();
|
||||
|
||||
var flickPosition = {};
|
||||
|
||||
if (caretA) {
|
||||
flickPosition.caretA = {
|
||||
offset: {
|
||||
x: newCaretPositions.caretA.x - caretPositions.caretA.x,
|
||||
y: newCaretPositions.caretA.y - caretPositions.caretA.y
|
||||
}
|
||||
};
|
||||
}
|
||||
if (caretB) {
|
||||
flickPosition.caretB = {
|
||||
offset: {
|
||||
x: newCaretPositions.caretB.x - caretPositions.caretB.x,
|
||||
y: newCaretPositions.caretB.y - caretPositions.caretB.y
|
||||
}
|
||||
};
|
||||
}
|
||||
this.moveCaretByPosition(flickPosition);
|
||||
},
|
||||
|
||||
selectionLocationHelper: function(locationType) {
|
||||
var rangeCount = this.rangeCount();
|
||||
var firstRectList = this.selectionRectList(0);
|
||||
var lastRectList = this.selectionRectList(rangeCount - 1);
|
||||
var lastListLength = lastRectList.length;
|
||||
var firstRect = firstRectList[0];
|
||||
var lastRect = lastRectList[lastListLength - 1];
|
||||
var originX = this.element.location.x;
|
||||
var originY = this.element.location.y;
|
||||
var startPos = 'left';
|
||||
var endPos = 'right';
|
||||
var startYOffset, endYOffset;
|
||||
|
||||
if (this.element.getAttribute('dir') === 'rtl') {
|
||||
startPos = 'right';
|
||||
endPos = 'left';
|
||||
}
|
||||
if (locationType === 'center') {
|
||||
startYOffset = firstRect.height / 2;
|
||||
endYOffset = lastRect.height / 2;
|
||||
} else if (locationType === 'caret') {
|
||||
startYOffset = firstRect.height + 5;
|
||||
endYOffset = lastRect.height + 5;
|
||||
} else {
|
||||
startYOffset = endYOffset = 0;
|
||||
}
|
||||
|
||||
return {
|
||||
caretA: {
|
||||
x: firstRect[startPos] - originX,
|
||||
y: firstRect.top + startYOffset - originY
|
||||
},
|
||||
caretB: {
|
||||
x: lastRect[endPos] - originX,
|
||||
y: lastRect.top + endYOffset - originY
|
||||
}
|
||||
};
|
||||
},
|
||||
|
||||
selectAll: function() {
|
||||
var cmd;
|
||||
if (this.isInputOrTextarea) {
|
||||
cmd = 'var len = arguments[0].value.length; arguments[0].focus();' +
|
||||
'arguments[0].setSelectionRange(0, len);';
|
||||
} else {
|
||||
cmd = 'var range = document.createRange();' +
|
||||
'range.setStart(arguments[0].firstChild, 0);' +
|
||||
'range.setEnd(arguments[0].lastChild, 0);' +
|
||||
'var sel = window.getSelection();' +
|
||||
'sel.removeAllRanges(); sel.addRange(range);';
|
||||
}
|
||||
this.client.executeScript(cmd, [this.element]);
|
||||
}
|
||||
};
|
||||
108
gaia/apps/system/test/marionette/lib/soundtoast.js
Executable file
108
gaia/apps/system/test/marionette/lib/soundtoast.js
Executable file
|
|
@ -0,0 +1,108 @@
|
|||
/* global module */
|
||||
'use strict';
|
||||
|
||||
function SoundToast(client) {
|
||||
this.client = client;
|
||||
}
|
||||
|
||||
module.exports = SoundToast;
|
||||
|
||||
SoundToast.Selector = Object.freeze({
|
||||
MediaVolumeBar: '#volume[data-channel="content"]',
|
||||
AlarmVolumeBar: '#volume[data-channel="alarm"]',
|
||||
NotificationVolumeBar: '#volume[data-channel="notification"]',
|
||||
TelephonyVolumeBar: '#volume[data-channel="telephony"]',
|
||||
BluetoothSCOVolumeBar: '#volume[data-channel="bt_sco"]',
|
||||
LoudWarningPrompt: '#dialog-message[data-l10n-id="ceWarningcontent"]'
|
||||
});
|
||||
|
||||
SoundToast.prototype = {
|
||||
client: null,
|
||||
|
||||
get MediaVolumeBar() {
|
||||
this.client.switchToFrame();
|
||||
return this.client.findElement(SoundToast.Selector.MediaVolumeBar);
|
||||
},
|
||||
|
||||
get AlarmVolumeBar() {
|
||||
this.client.switchToFrame();
|
||||
return this.client.findElement(SoundToast.Selector.AlarmVolumeBar);
|
||||
},
|
||||
|
||||
get NotificationVolumeBar() {
|
||||
this.client.switchToFrame();
|
||||
return this.client.findElement(SoundToast.Selector.NotificationVolumeBar);
|
||||
},
|
||||
|
||||
get TelephonyVolumeBar() {
|
||||
this.client.switchToFrame();
|
||||
return this.client.findElement(SoundToast.Selector.TelephonyVolumeBar);
|
||||
},
|
||||
|
||||
get BluetoothSCOVolumeBar() {
|
||||
this.client.switchToFrame();
|
||||
return this.client.findElement(SoundToast.Selector.BluetoothSCOVolumeBar);
|
||||
},
|
||||
|
||||
get LoudWarningPrompt() {
|
||||
this.client.switchToFrame();
|
||||
return this.client.findElement(SoundToast.Selector.LoudWarningPrompt);
|
||||
},
|
||||
|
||||
waitForMediaVolumeShown: function(shouldBeShown, shouldBeMuted) {
|
||||
this.client.waitFor(function() {
|
||||
var volumeShown = this.MediaVolumeBar.displayed();
|
||||
var classes = this.MediaVolumeBar.getAttribute('class');
|
||||
var result = ((classes.indexOf('mute') !== -1) === shouldBeMuted);
|
||||
return (volumeShown && result) === shouldBeShown;
|
||||
}.bind(this));
|
||||
},
|
||||
|
||||
waitForAlarmVolumeShown: function(shouldBeShown, shouldBeMuted) {
|
||||
this.client.waitFor(function() {
|
||||
var volumeShown = this.AlarmVolumeBar.displayed();
|
||||
var classes = this.AlarmVolumeBar.getAttribute('class');
|
||||
var result = ((classes.indexOf('mute') !== -1) === shouldBeMuted);
|
||||
return (volumeShown && result) === shouldBeShown;
|
||||
}.bind(this));
|
||||
},
|
||||
|
||||
waitForNotificationVolumeShown:
|
||||
function(shouldBeShown, shouldBeMuted, shouldBeVibrated) {
|
||||
this.client.waitFor(function() {
|
||||
var bar = this.NotificationVolumeBar;
|
||||
var volumeShown = bar.displayed();
|
||||
var classes = bar.getAttribute('class');
|
||||
var checkedMute = ((classes.indexOf('mute') !== -1) === shouldBeMuted);
|
||||
var checkedVibration =
|
||||
((classes.indexOf('vibration') !== -1) === shouldBeVibrated);
|
||||
var result = (volumeShown && checkedMute && checkedVibration);
|
||||
|
||||
return result === shouldBeShown;
|
||||
}.bind(this));
|
||||
},
|
||||
|
||||
waitForTelephonyVolumeShown: function(shouldBeShown) {
|
||||
this.client.waitFor(function() {
|
||||
var volumeShown = this.TelephonyVolumeBar.displayed();
|
||||
return volumeShown === shouldBeShown;
|
||||
}.bind(this));
|
||||
},
|
||||
|
||||
waitForBluetoothSCOVolumeShown: function(shouldBeShown) {
|
||||
this.client.waitFor(function() {
|
||||
var volumeShown = this.BluetoothSCOVolumeBar.displayed();
|
||||
return volumeShown === shouldBeShown;
|
||||
}.bind(this));
|
||||
},
|
||||
|
||||
waitForLoudWarningShown: function() {
|
||||
this.client.waitFor(function() {
|
||||
var loudWarningPrompt = this.LoudWarningPrompt;
|
||||
if (!loudWarningPrompt.displayed()) {
|
||||
return false;
|
||||
}
|
||||
return loudWarningPrompt.getAttribute('textContent').length > 0;
|
||||
}.bind(this));
|
||||
},
|
||||
};
|
||||
235
gaia/apps/system/test/marionette/lib/statusbar.js
Executable file
235
gaia/apps/system/test/marionette/lib/statusbar.js
Executable file
|
|
@ -0,0 +1,235 @@
|
|||
/* global module */
|
||||
/* exported StatusBar */
|
||||
|
||||
'use strict';
|
||||
|
||||
(function(module) {
|
||||
|
||||
var StatusBar = function(client) {
|
||||
this.client = client;
|
||||
this.init();
|
||||
};
|
||||
|
||||
StatusBar.Selector = Object.freeze({
|
||||
'operator': '.sb-icon-operator',
|
||||
'sms': '.sb-icon-sms',
|
||||
'alarm': '.sb-icon-alarm',
|
||||
'playing': '.sb-icon-playing',
|
||||
'headphone': '.sb-icon-headphone',
|
||||
'bluetooth-headphone': '.sb-icon-bluetooth-headphone',
|
||||
'call-forwardings': '.sb-icon-call-forwarding',
|
||||
'geolocation': '.sb-icon-geolocation',
|
||||
'recording': '.sb-icon-recording',
|
||||
'mute': '.sb-icon-mute',
|
||||
'usb': '.sb-icon-usb',
|
||||
'download': '.sb-icon-download',
|
||||
'emergency-callback': '.sb-icon-emergency-callback',
|
||||
'nfc': '.sb-icon-nfc',
|
||||
'bluetooth-transfer': '.sb-icon-bluetooth-transfer',
|
||||
'bluetooth': '.sb-icon-bluetooth',
|
||||
'tethering': '.sb-icon-tethering',
|
||||
'network-activity': '.sb-icon-network-activity',
|
||||
'mobile-connection': '.sb-icon-mobile-connection',
|
||||
'wifi': '.sb-icon-wifi',
|
||||
'airplane-mode': '.sb-icon-airplane-mode',
|
||||
'battery': '.sb-icon-battery',
|
||||
'time': '.sb-icon-time',
|
||||
'debugging': '.sb-icon-debugging',
|
||||
|
||||
statusbar: '#statusbar',
|
||||
statusbarIcons: '#statusbar-icons'
|
||||
});
|
||||
|
||||
StatusBar.prototype = {
|
||||
kActiveIndicatorTimeout: null,
|
||||
client: null,
|
||||
|
||||
get Icons() {
|
||||
return this.client.executeScript(function() {
|
||||
return window.wrappedJSObject.Statusbar.PRIORITIES;
|
||||
});
|
||||
},
|
||||
|
||||
get maximizedStatusbar() {
|
||||
var statusbar = StatusBar.Selector.statusbarIcons;
|
||||
return this.client.findElement(statusbar);
|
||||
},
|
||||
|
||||
showAllRunningIcons: function() {
|
||||
this.Icons.forEach(function(iconName) {
|
||||
if (this[iconName].icon) {
|
||||
this[iconName].icon.scriptWith(function(icon) {
|
||||
icon.hidden = false;
|
||||
});
|
||||
}
|
||||
}.bind(this));
|
||||
},
|
||||
|
||||
isVisible: function() {
|
||||
var el = this.client.findElement(StatusBar.Selector.statusbar);
|
||||
return el.displayed() && el.location().y === 0;
|
||||
},
|
||||
|
||||
waitForAppear: function() {
|
||||
this.client.waitFor(function() {
|
||||
return this.isVisible();
|
||||
}.bind(this));
|
||||
},
|
||||
|
||||
waitForDisappear: function() {
|
||||
this.client.waitFor(function() {
|
||||
return !this.isVisible();
|
||||
}.bind(this));
|
||||
},
|
||||
|
||||
/**
|
||||
* Change the delay value in StatusBar.
|
||||
* @param {number=} delay The new value for delay in milliseconds.
|
||||
*/
|
||||
changeDelayValue: function(iconName, delay) {
|
||||
var self = this;
|
||||
delay = parseInt(delay, 10);
|
||||
delay = !isNaN(delay) ? delay : 100;
|
||||
this.client.executeScript(function(iconName, delay) {
|
||||
self.kActiveIndicatorTimeout =
|
||||
window.wrappedJSObject[iconName].protoype.kActiveIndicatorTimeout;
|
||||
window.wrappedJSObject[iconName].prototype.kActiveIndicatorTimeout =
|
||||
delay;
|
||||
}, [iconName, delay]);
|
||||
},
|
||||
|
||||
/**
|
||||
* Restore the delay in StatusBar to its original value.
|
||||
*/
|
||||
restoreDelayValue: function() {
|
||||
if (this.kActiveIndicatorTimeout === null) {
|
||||
return;
|
||||
}
|
||||
|
||||
var self = this;
|
||||
this.client.executeScript(function() {
|
||||
window.wrappedJSObject.Statusbar.kActiveIndicatorTimeout =
|
||||
self.kActiveIndicatorTimeout;
|
||||
self.kActiveIndicatorTimeout = null;
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* Dispatch an event of type `eventType` from window.
|
||||
* @param {string} eventType
|
||||
*/
|
||||
dispatchEvent: function(eventType) {
|
||||
this.client.executeScript(function(eventType) {
|
||||
var evt = new CustomEvent(eventType);
|
||||
window.wrappedJSObject.dispatchEvent(evt);
|
||||
}, [eventType]);
|
||||
},
|
||||
|
||||
/**
|
||||
* Dispatch a `mozChromeEvent` of type `eventType` with the detail object
|
||||
* `detail` from window.
|
||||
* @param {string} eventType
|
||||
* @param {Object} detail
|
||||
*/
|
||||
dispatchMozChromeEvent: function(eventType, detail) {
|
||||
detail = (detail !== undefined) ? detail : {};
|
||||
detail.type = eventType;
|
||||
|
||||
this.client.executeScript(function(detail) {
|
||||
window.wrappedJSObject.Service.debug('will dispatch moz chrome event.');
|
||||
var evt = new CustomEvent('mozChromeEvent', {
|
||||
detail: detail
|
||||
});
|
||||
window.wrappedJSObject.dispatchEvent(evt);
|
||||
window.wrappedJSObject.Service.debug(' moz chrome event dispatched');
|
||||
}, [detail]);
|
||||
},
|
||||
|
||||
/**
|
||||
* Initialize the helpers.
|
||||
*/
|
||||
init: function() {
|
||||
var self = this;
|
||||
|
||||
// Status bar icons have the following set of helpers:
|
||||
// * statusBar.alarm.icon
|
||||
// return the DOM element
|
||||
// * statusBar.alarm.show()
|
||||
// show the icon
|
||||
// * statusBar.alarm.hide()
|
||||
// hide it
|
||||
// * statusBar.alarm.waitForIconToAppear()
|
||||
// wait until the element appears
|
||||
// * statusBar.alarm.waitForIconToDisappear()
|
||||
// wait until it disappears
|
||||
|
||||
// Maximised status bar
|
||||
this.minimised = {};
|
||||
this.Icons.forEach(function(iconName) {
|
||||
this[iconName] = {
|
||||
get icon() {
|
||||
try {
|
||||
var client = self.client.scope({ searchTimeout: 100 });
|
||||
return client.findElement('#statusbar-icons ' +
|
||||
StatusBar.Selector[iconName]);
|
||||
} catch (e) {
|
||||
return null;
|
||||
}
|
||||
},
|
||||
show: showIconGenerator.call(this),
|
||||
hide: hideIconGenerator.call(this),
|
||||
waitForIconToAppear: waitForIconToAppearGenerator.call(this),
|
||||
waitForIconToDisappear: waitForIconToDisappearGenerator.call(this)
|
||||
};
|
||||
}.bind(this));
|
||||
|
||||
// Functions generating helper functions.
|
||||
function showIconGenerator() {
|
||||
return function() {
|
||||
var icon = this.icon;
|
||||
self.client.executeScript(function(icon) {
|
||||
icon.hidden = false;
|
||||
}, [icon]);
|
||||
};
|
||||
}
|
||||
|
||||
function hideIconGenerator() {
|
||||
return function() {
|
||||
var icon = this.icon;
|
||||
self.client.executeScript(function(icon) {
|
||||
icon.hidden = true;
|
||||
}, [icon]);
|
||||
};
|
||||
}
|
||||
|
||||
function waitForIconToAppearGenerator() {
|
||||
return function() {
|
||||
var icon = this.icon;
|
||||
self.client.waitFor(function() {
|
||||
var display = icon.scriptWith(function(element) {
|
||||
return window.getComputedStyle(element).display;
|
||||
});
|
||||
return display !== 'none';
|
||||
});
|
||||
return icon;
|
||||
};
|
||||
}
|
||||
|
||||
function waitForIconToDisappearGenerator() {
|
||||
return function() {
|
||||
var icon = this.icon;
|
||||
self.client.waitFor(function() {
|
||||
var display = icon.scriptWith(function(element) {
|
||||
return window.getComputedStyle(element).display;
|
||||
});
|
||||
return display === 'none';
|
||||
});
|
||||
return icon;
|
||||
};
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
module.exports = StatusBar;
|
||||
|
||||
})(module);
|
||||
642
gaia/apps/system/test/marionette/lib/system.js
Executable file
642
gaia/apps/system/test/marionette/lib/system.js
Executable file
|
|
@ -0,0 +1,642 @@
|
|||
'use strict';
|
||||
|
||||
function System(client) {
|
||||
this.client = client.scope({
|
||||
searchTimeout: 20000
|
||||
});
|
||||
}
|
||||
|
||||
module.exports = System;
|
||||
|
||||
System.URL = 'app://system.gaiamobile.org/manifest.webapp';
|
||||
|
||||
System.Selector = Object.freeze({
|
||||
screen: '#screen',
|
||||
activeHomescreenFrame: '#homescreen.appWindow.active',
|
||||
appAuthDialog: '.appWindow.active .authentication-dialog.visible',
|
||||
// XXX: Gaia-header covers the back button, so you can't tap on it.
|
||||
// This is a problem with gaia-header, so instead we return the gaia-header,
|
||||
// And manually tap on the expected coordinate.
|
||||
appAuthDialogCancel: '.appWindow.active .authentication-dialog.visible ' +
|
||||
'gaia-header',
|
||||
appAuthDialogLogin: '.appWindow.active .authentication-dialog.visible ' +
|
||||
'button.authentication-dialog-http-authentication-ok',
|
||||
appAuthDialogUsername: '.appWindow.active .authentication-dialog.visible ' +
|
||||
'.authentication-dialog-http-username-input',
|
||||
appAuthDialogPassword: '.appWindow.active .authentication-dialog.visible ' +
|
||||
'.authentication-dialog-http-password-input',
|
||||
appContextMenuSaveLink:
|
||||
'.appWindow.active .contextmenu [data-id="save-link"]',
|
||||
appWindow: '.appWindow',
|
||||
appTitlebar: '.appWindow.active .titlebar',
|
||||
appUrlbar: '.appWindow.active .title',
|
||||
appChrome: '.appWindow.active .chrome',
|
||||
appChromeBack: '.appWindow.active .back-button',
|
||||
appChromeForward: '.appWindow.active .forward-button',
|
||||
// We never want the menu button from the Homscreen appWindow even
|
||||
// though it's still selectable. Explicity disallow it in the query.
|
||||
appChromeContextLink: '.appWindow.active:not(.homescreen) .menu-button',
|
||||
appChromeContextMenu: '.appWindow.active .contextmenu',
|
||||
appChromeContextNewPrivate: '.appWindow.active [data-id=new-private-window]',
|
||||
appChromeContextMenuNewWindow: '.appWindow.active [data-id=new-window]',
|
||||
appChromeContextMenuBookmark:
|
||||
'.appWindow.active:not(.homescreen) [data-id=add-to-homescreen]',
|
||||
appChromeContextMenuPin: '.appWindow.active [data-id=pin-to-home-screen]',
|
||||
appChromeContextMenuShare: '.appWindow.active [data-id=share]',
|
||||
appChromeContextMenuSaveImage: '.appWindow.active [data-id=save-image]',
|
||||
appChromeContextMenuCancel: '.appWindow.active #ctx-cancel-button',
|
||||
appChromeReloadButton: '.appWindow.active .controls .reload-button',
|
||||
appChromeStopButton: '.appWindow.active .controls .stop-button',
|
||||
appChromeWindowsButton: '.appWindow.active .controls .windows-button',
|
||||
appChromeProgressBar: '.appWindow.active .chrome gaia-progress',
|
||||
browserWindow: '.appWindow.browser',
|
||||
currentWindow: '.appWindow.active',
|
||||
dialogOverlay: '#screen #dialog-overlay',
|
||||
downloadDialog: '#downloadConfirmUI',
|
||||
imeMenu: '.ime-menu',
|
||||
inlineActivity: '.appWindow.inline-activity',
|
||||
permissionDialog: '#permission-dialog',
|
||||
permissionYes: '#permission-yes',
|
||||
pinDialog: '.appWindow.active .chrome .pin-dialog',
|
||||
pinButton: '.appWindow.active .chrome .pin-button',
|
||||
sleepMenuContainer: '#sleep-menu-container',
|
||||
softwareButtons: '#software-buttons',
|
||||
softwareHome: '#software-home-button',
|
||||
softwareHomeFullscreen: '#fullscreen-software-home-button',
|
||||
softwareHomeFullscreenLayout: '#software-buttons-fullscreen-layout',
|
||||
statusbar: '#statusbar',
|
||||
statusbarShadow: '.appWindow.active > .titlebar .statusbar-shadow',
|
||||
statusbarShadowTray: '#statusbar-tray',
|
||||
statusbarShadowActivity: '.activityWindow.active .statusbar-shadow',
|
||||
statusbarIcons: '#statusbar-icons',
|
||||
statusbarOperator: '.statusbar-operator',
|
||||
systemBanner: '#screen > gaia-toast.banner',
|
||||
toaster: '#notification-toaster',
|
||||
topPanel: '#top-panel',
|
||||
trustedWindow: '.appWindow.active.trustedwindow',
|
||||
trustedWindowChrome: '.appWindow.active.trustedwindow .chrome',
|
||||
leftPanel: '#left-panel',
|
||||
rightPanel: '#right-panel',
|
||||
siteIcon: '.appWindow.active .chrome .site-icon',
|
||||
utilityTray: '#utility-tray',
|
||||
utilityTrayMotion: '#utility-tray-motion',
|
||||
visibleForm: '#action-menu > form.visible',
|
||||
cancelActivity: '#action-menu form.visible button[data-action="cancel"]',
|
||||
nfcIcon: '.statusbar-nfc',
|
||||
activeKeyboard: '.inputWindow.active'
|
||||
});
|
||||
|
||||
System.prototype = {
|
||||
client: null,
|
||||
|
||||
URL: System.URL,
|
||||
origin: System.URL.substring(0, System.URL.indexOf('/manifest.webapp')),
|
||||
|
||||
Selector: System.Selector,
|
||||
|
||||
getAppWindows: function() {
|
||||
return this.client.findElements(System.Selector.appWindow);
|
||||
},
|
||||
|
||||
getBrowserWindows: function() {
|
||||
var searchTimeout = this.client.searchTimeout;
|
||||
this.client.setSearchTimeout(0);
|
||||
|
||||
var elements = this.client.findElements(System.Selector.browserWindow);
|
||||
|
||||
this.client.setSearchTimeout(searchTimeout);
|
||||
return elements;
|
||||
},
|
||||
|
||||
get activeHomescreenFrame() {
|
||||
var homescreen = System.Selector.activeHomescreenFrame;
|
||||
return this.client.helper.waitForElement(homescreen);
|
||||
},
|
||||
|
||||
get appAuthDialog() {
|
||||
return this.client.helper.waitForElement(System.Selector.appAuthDialog);
|
||||
},
|
||||
|
||||
get appAuthDialogCancel() {
|
||||
return this.client.helper.waitForElement(
|
||||
System.Selector.appAuthDialogCancel);
|
||||
},
|
||||
|
||||
get appAuthDialogLogin() {
|
||||
return this.client.helper.waitForElement(
|
||||
System.Selector.appAuthDialogLogin);
|
||||
},
|
||||
|
||||
get appAuthDialogUsername() {
|
||||
return this.client.helper.waitForElement(
|
||||
System.Selector.appAuthDialogUsername);
|
||||
},
|
||||
|
||||
get appAuthDialogPassword() {
|
||||
return this.client.helper.waitForElement(
|
||||
System.Selector.appAuthDialogPassword);
|
||||
},
|
||||
|
||||
get appContextMenuSaveLink() {
|
||||
return this.client.helper.waitForElement(
|
||||
System.Selector.appContextMenuSaveLink);
|
||||
},
|
||||
|
||||
get appTitlebar() {
|
||||
return this.client.helper.waitForElement(System.Selector.appTitlebar);
|
||||
},
|
||||
|
||||
get appUrlbar() {
|
||||
return this.client.helper.waitForElement(System.Selector.appUrlbar);
|
||||
},
|
||||
|
||||
get appChrome() {
|
||||
return this.client.helper.waitForElement(
|
||||
System.Selector.appChrome);
|
||||
},
|
||||
|
||||
get appChromeBack() {
|
||||
return this.client.helper.waitForElement(
|
||||
System.Selector.appChromeBack);
|
||||
},
|
||||
|
||||
get appChromeForward() {
|
||||
return this.client.helper.waitForElement(
|
||||
System.Selector.appChromeForward);
|
||||
},
|
||||
|
||||
get appChromeContextLink() {
|
||||
return this.client.helper.waitForElement(
|
||||
System.Selector.appChromeContextLink);
|
||||
},
|
||||
|
||||
get appChromeContextMenu() {
|
||||
return this.client.helper.waitForElement(
|
||||
System.Selector.appChromeContextMenu);
|
||||
},
|
||||
|
||||
get appChromeContextNewPrivate() {
|
||||
return this.client.helper.waitForElement(
|
||||
System.Selector.appChromeContextNewPrivate);
|
||||
},
|
||||
|
||||
get appChromeContextMenuNewWindow() {
|
||||
return this.client.helper.waitForElement(
|
||||
System.Selector.appChromeContextMenuNewWindow);
|
||||
},
|
||||
|
||||
get appChromeContextMenuBookmark() {
|
||||
return this.client.helper.waitForElement(
|
||||
System.Selector.appChromeContextMenuBookmark);
|
||||
},
|
||||
|
||||
get appChromeContextMenuPin() {
|
||||
return this.client.helper.waitForElement(
|
||||
System.Selector.appChromeContextMenuPin);
|
||||
},
|
||||
|
||||
get appChromeContextMenuShare() {
|
||||
return this.client.helper.waitForElement(
|
||||
System.Selector.appChromeContextMenuShare);
|
||||
},
|
||||
|
||||
get appChromeContextMenuSaveImage() {
|
||||
return this.client.helper.waitForElement(
|
||||
System.Selector.appChromeContextMenuSaveImage);
|
||||
},
|
||||
|
||||
get appChromeReloadButton() {
|
||||
return this.client.helper.waitForElement(
|
||||
System.Selector.appChromeReloadButton);
|
||||
},
|
||||
|
||||
get appChromeStopButton() {
|
||||
return this.client.helper.waitForElement(
|
||||
System.Selector.appChromeStopButton);
|
||||
},
|
||||
|
||||
get appChromeContextMenuCancel() {
|
||||
return this.client.helper.waitForElement(
|
||||
System.Selector.appChromeContextMenuCancel);
|
||||
},
|
||||
|
||||
get appChromeProgressBar() {
|
||||
return this.client.helper.waitForElement(
|
||||
System.Selector.appChromeProgressBar);
|
||||
},
|
||||
|
||||
get currentWindow() {
|
||||
return this.client.helper.waitForElement(System.Selector.currentWindow);
|
||||
},
|
||||
|
||||
get dialogOverlay() {
|
||||
return this.client.helper.waitForElement(
|
||||
System.Selector.dialogOverlay);
|
||||
},
|
||||
|
||||
get downloadDialog() {
|
||||
return this.client.helper.waitForElement(System.Selector.downloadDialog);
|
||||
},
|
||||
|
||||
get imeMenu() {
|
||||
return this.client.helper.waitForElement(System.Selector.imeMenu);
|
||||
},
|
||||
|
||||
get inlineActivity() {
|
||||
return this.client.helper.waitForElement(System.Selector.inlineActivity);
|
||||
},
|
||||
|
||||
get permissionDialog() {
|
||||
return this.client.helper.waitForElement(System.Selector.permissionDialog);
|
||||
},
|
||||
|
||||
get permissionYes() {
|
||||
return this.client.helper.waitForElement(System.Selector.permissionYes);
|
||||
},
|
||||
|
||||
get pinDialog() {
|
||||
return this.client.findElement(System.Selector.pinDialog);
|
||||
},
|
||||
|
||||
get pinButton() {
|
||||
return this.client.findElement(System.Selector.pinButton);
|
||||
},
|
||||
|
||||
get sleepMenuContainer() {
|
||||
return this.client.helper.waitForElement(
|
||||
System.Selector.sleepMenuContainer);
|
||||
},
|
||||
|
||||
get softwareButtons() {
|
||||
return this.client.findElement(System.Selector.softwareButtons);
|
||||
},
|
||||
|
||||
get softwareHome() {
|
||||
return this.client.findElement(System.Selector.softwareHome);
|
||||
},
|
||||
|
||||
get softwareHomeFullscreen() {
|
||||
return this.client.findElement(System.Selector.softwareHomeFullscreen);
|
||||
},
|
||||
|
||||
get softwareHomeFullscreenLayout() {
|
||||
return this.client.findElement(
|
||||
System.Selector.softwareHomeFullscreenLayout);
|
||||
},
|
||||
|
||||
get statusbar() {
|
||||
return this.client.findElement(System.Selector.statusbar);
|
||||
},
|
||||
|
||||
get statusbarIcons() {
|
||||
return this.client.helper.waitForElement(System.Selector.statusbarIcons);
|
||||
},
|
||||
|
||||
get statusbarOperator() {
|
||||
return this.client.findElement(System.Selector.statusbarOperator);
|
||||
},
|
||||
|
||||
get systemBanner() {
|
||||
return this.client.helper.waitForElement(System.Selector.systemBanner);
|
||||
},
|
||||
|
||||
get trustedWindow() {
|
||||
return this.client.helper.waitForElement(
|
||||
System.Selector.trustedWindow);
|
||||
},
|
||||
|
||||
get trustedWindowChrome() {
|
||||
return this.client.helper.waitForElement(
|
||||
System.Selector.trustedWindowChrome);
|
||||
},
|
||||
|
||||
get utilityTray() {
|
||||
return this.client.findElement(System.Selector.utilityTray);
|
||||
},
|
||||
|
||||
get utilityTrayMotion() {
|
||||
return this.client.findElement(System.Selector.utilityTrayMotion);
|
||||
},
|
||||
|
||||
get toaster() {
|
||||
var toaster;
|
||||
this.client.waitFor(function() {
|
||||
toaster = this.client.findElement(System.Selector.toaster);
|
||||
return toaster ? true : false;
|
||||
}.bind(this));
|
||||
|
||||
return toaster;
|
||||
},
|
||||
|
||||
get topPanel() {
|
||||
return this.client.findElement(System.Selector.topPanel);
|
||||
},
|
||||
|
||||
get leftPanel() {
|
||||
return this.client.findElement(System.Selector.leftPanel);
|
||||
},
|
||||
|
||||
get rightPanel() {
|
||||
return this.client.findElement(System.Selector.rightPanel);
|
||||
},
|
||||
|
||||
get visibleForm() {
|
||||
return this.client.findElement(System.Selector.visibleForm);
|
||||
},
|
||||
|
||||
get cancelActivity() {
|
||||
return this.client.findElement(System.Selector.cancelActivity);
|
||||
},
|
||||
|
||||
get screenSize() {
|
||||
return this.client.findElement(System.Selector.screen).size();
|
||||
},
|
||||
|
||||
get siteIcon() {
|
||||
return this.client.helper.waitForElement(System.Selector.siteIcon);
|
||||
},
|
||||
|
||||
get nfcIcon() {
|
||||
return this.client.findElement(System.Selector.nfcIcon);
|
||||
},
|
||||
|
||||
get statusbarShadow() {
|
||||
return this.client.findElement(System.Selector.statusbarShadow);
|
||||
},
|
||||
|
||||
get statusbarShadowTray() {
|
||||
return this.client.findElement(System.Selector.statusbarShadowTray);
|
||||
},
|
||||
|
||||
get statusbarShadowActivity() {
|
||||
return this.client.findElement(System.Selector.statusbarShadowActivity);
|
||||
},
|
||||
|
||||
getAppIframe: function(url) {
|
||||
var iframe = this.client.findElement('iframe[src*="' + url + '"]');
|
||||
|
||||
iframe.ariaDisplayed = function() {
|
||||
return this.getAttribute('aria-hidden') !== 'true';
|
||||
};
|
||||
|
||||
return iframe;
|
||||
},
|
||||
|
||||
shareLink: function() {
|
||||
this.appChromeContextLink.tap();
|
||||
this.client.waitFor(function() {
|
||||
return this.appChromeContextMenu.displayed();
|
||||
}.bind(this));
|
||||
this.appChromeContextMenuShare.tap();
|
||||
this.waitForActivityMenu();
|
||||
},
|
||||
|
||||
saveImage: function() {
|
||||
this.client.switchToFrame();
|
||||
this.client.waitFor(function() {
|
||||
return this.appChromeContextMenu.displayed();
|
||||
}.bind(this));
|
||||
this.appChromeContextMenuSaveImage.tap();
|
||||
},
|
||||
|
||||
getSdCardFilesMatching: function(name) {
|
||||
return this.client.executeAsyncScript(function(name) {
|
||||
var navigator = window.wrappedJSObject.navigator;
|
||||
var storage = navigator.getDeviceStorage('sdcard');
|
||||
var req = storage.enumerate();
|
||||
var matchingFiles = [];
|
||||
req.onsuccess = function() {
|
||||
var file = req.result;
|
||||
if (file) {
|
||||
if (file.indexOf(name) > 1) {
|
||||
matchingFiles.push(file);
|
||||
}
|
||||
req.continue();
|
||||
} else {
|
||||
marionetteScriptFinished(matchingFiles);
|
||||
}
|
||||
};
|
||||
}, [name]);
|
||||
},
|
||||
|
||||
/**
|
||||
* Clicks the bottom of the screen, where we expect the software home button
|
||||
* to exist. There are several different variations in the same spot, this
|
||||
* allows us to try to click all of them.
|
||||
*/
|
||||
clickSoftwareHomeButton: function() {
|
||||
var body = this.client.findElement('body');
|
||||
var screenSize = body.scriptWith(function(el) {
|
||||
return el.getBoundingClientRect();
|
||||
});
|
||||
body.tap(screenSize.width / 2, screenSize.height - 10);
|
||||
},
|
||||
|
||||
gotoBrowser: function(url) {
|
||||
this.client.switchToFrame();
|
||||
var frame = this.waitForUrlLoaded(url);
|
||||
this.client.switchToFrame(frame);
|
||||
this.client.helper.waitForElement('body');
|
||||
},
|
||||
|
||||
waitForUrlLoaded: function(url) {
|
||||
var frame = this.client.helper.waitForElement(
|
||||
'div[transition-state="opened"] iframe[src="' + url + '"]');
|
||||
return frame;
|
||||
},
|
||||
|
||||
closeBrowserByUrl: function(url) {
|
||||
var winId = this.client.executeScript(function(url) {
|
||||
var win = window.wrappedJSObject;
|
||||
var activeApp = win.appWindowManager.getActiveApp();
|
||||
activeApp.kill();
|
||||
return activeApp.element.id;
|
||||
}, [url]);
|
||||
this.client.helper.waitForElementToDisappear('#' + winId);
|
||||
this.client.switchToFrame();
|
||||
},
|
||||
|
||||
goBack: function(url) {
|
||||
this.client.switchToFrame();
|
||||
this.appChromeBack.tap();
|
||||
},
|
||||
|
||||
goForward: function(url) {
|
||||
this.client.switchToFrame();
|
||||
this.appChromeForward.tap();
|
||||
},
|
||||
|
||||
getHomescreenIframe: function() {
|
||||
return this.client.findElement('#homescreen iframe');
|
||||
},
|
||||
|
||||
waitForLaunch: function(url) {
|
||||
this.client.apps.launch(url);
|
||||
var iframe = this.getAppIframe(url);
|
||||
this.client.waitFor(function() {
|
||||
return iframe.displayed();
|
||||
});
|
||||
|
||||
iframe.ariaDisplayed = function() {
|
||||
return this.getAttribute('aria-hidden') !== 'true';
|
||||
};
|
||||
|
||||
return iframe;
|
||||
},
|
||||
|
||||
waitForStartup: function() {
|
||||
this.client.helper.waitForElementToDisappear('#os-logo');
|
||||
},
|
||||
|
||||
waitForFullyLoaded: function() {
|
||||
var body = this.client.findElement('body');
|
||||
this.client.waitFor(function() {
|
||||
return body.getAttribute('ready-state') == 'fullyLoaded';
|
||||
}, { timeout: 120000 });
|
||||
},
|
||||
|
||||
// Since the getScreenshot call is asynchronous and does not have any
|
||||
// external side effect, we're just queuing another screenshot request
|
||||
// afterward to be sure it's done.
|
||||
waitUntilScreenshotable: function(iframe) {
|
||||
this.client.executeAsyncScript(function(iframe) {
|
||||
iframe.wrappedJSObject.getScreenshot(1, 1).then(marionetteScriptFinished,
|
||||
marionetteScriptFinished);
|
||||
}, [iframe]);
|
||||
},
|
||||
|
||||
waitForKeyboard: function() {
|
||||
this.client.switchToFrame();
|
||||
this.client.helper.waitForElement(System.Selector.activeKeyboard);
|
||||
},
|
||||
|
||||
waitForKeyboardToDisappear: function() {
|
||||
this.client.helper.waitForElementToDisappear(
|
||||
System.Selector.activeKeyboard
|
||||
);
|
||||
},
|
||||
|
||||
goHome: function() {
|
||||
this.client.switchToFrame();
|
||||
this.client.executeAsyncScript(function() {
|
||||
var win = window.wrappedJSObject;
|
||||
win.addEventListener('homescreenopened', function trWait() {
|
||||
win.removeEventListener('homescreenopened', trWait);
|
||||
marionetteScriptFinished();
|
||||
});
|
||||
win.dispatchEvent(new CustomEvent('home'));
|
||||
});
|
||||
},
|
||||
|
||||
getActiveAppName: function() {
|
||||
var activeApp = this.client.executeScript(function() {
|
||||
var win = window.wrappedJSObject;
|
||||
var activeApp = win.appWindowManager.getActiveApp();
|
||||
return activeApp.name;
|
||||
});
|
||||
return activeApp;
|
||||
},
|
||||
|
||||
tapHome: function() {
|
||||
this.client.switchToFrame();
|
||||
this.client.executeScript(function() {
|
||||
window.wrappedJSObject.dispatchEvent(new CustomEvent('home'));
|
||||
});
|
||||
},
|
||||
|
||||
holdHome: function() {
|
||||
this.client.switchToFrame();
|
||||
this.client.executeScript(function() {
|
||||
window.wrappedJSObject.dispatchEvent(new CustomEvent('holdhome'));
|
||||
});
|
||||
},
|
||||
|
||||
resize: function() {
|
||||
this.client.switchToFrame();
|
||||
this.client.executeScript(function() {
|
||||
window.wrappedJSObject.dispatchEvent(new CustomEvent('resize'));
|
||||
});
|
||||
},
|
||||
|
||||
request: function(service) {
|
||||
this.client.executeScript(function(service) {
|
||||
window.wrappedJSObject.Service.request(service);
|
||||
}, [service]);
|
||||
},
|
||||
|
||||
turnScreenOn: function() {
|
||||
this.client.switchToFrame();
|
||||
this.client.executeScript(function() {
|
||||
window.wrappedJSObject.Service.request(
|
||||
'turnScreenOn');
|
||||
});
|
||||
},
|
||||
|
||||
turnScreenOff: function() {
|
||||
this.client.switchToFrame();
|
||||
this.client.executeScript(function() {
|
||||
window.wrappedJSObject.Service.request(
|
||||
'turnScreenOff', true, 'powerkey');
|
||||
});
|
||||
},
|
||||
|
||||
stopClock: function() {
|
||||
var client = this.client;
|
||||
var clock = this.client.findElement('#statusbar-time');
|
||||
client.executeScript(function() {
|
||||
window.wrappedJSObject.Service.request('TimeIcon:stop');
|
||||
});
|
||||
client.waitFor(function() {
|
||||
return !clock.displayed();
|
||||
});
|
||||
},
|
||||
|
||||
stopDevtools: function() {
|
||||
this.client.executeScript(function() {
|
||||
window.wrappedJSObject.Service.request('DeveloperHud:stop');
|
||||
});
|
||||
},
|
||||
|
||||
// The activity menu has a transform transition where it appears from the
|
||||
// bottom of the screen. This waits for that to complete.
|
||||
waitForActivityMenu: function() {
|
||||
var form = this.client.helper.waitForElement(System.Selector.visibleForm);
|
||||
this.client.waitFor(function() {
|
||||
return form.scriptWith(function(element) {
|
||||
return window.getComputedStyle(element).transform ===
|
||||
'matrix(1, 0, 0, 1, 0, 0)';
|
||||
});
|
||||
});
|
||||
},
|
||||
|
||||
waitForBrowser: function waitForBrowser(url) {
|
||||
return this.client.helper.waitForElement(
|
||||
'div[transition-state="opened"] iframe[src="' + url + '"]');
|
||||
},
|
||||
|
||||
// It looks for an activity menu, and returns the first
|
||||
// button element which includes with str
|
||||
getActivityOptionMatching: function(str) {
|
||||
var activityMenuSelector = System.Selector.visibleForm + ' button.icon';
|
||||
var options = this.client.findElements(activityMenuSelector);
|
||||
for (var i = 0; i < options.length; i++) {
|
||||
if (options[i].getAttribute('style').indexOf(str) > 1) {
|
||||
return options[i];
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
sendSystemUpdateNotification: function() {
|
||||
this.client.executeScript(function() {
|
||||
var UpdateManager = window.wrappedJSObject.UpdateManager;
|
||||
UpdateManager.addToUpdatesQueue(UpdateManager.systemUpdatable);
|
||||
UpdateManager.displayNotificationAndToaster();
|
||||
});
|
||||
},
|
||||
|
||||
dismissBanner: function() {
|
||||
var banner = this.systemBanner;
|
||||
banner.tap();
|
||||
this.client.helper.waitForElementToDisappear(banner);
|
||||
}
|
||||
};
|
||||
84
gaia/apps/system/test/marionette/lib/task_manager.js
Executable file
84
gaia/apps/system/test/marionette/lib/task_manager.js
Executable file
|
|
@ -0,0 +1,84 @@
|
|||
'use strict';
|
||||
|
||||
(function(module) {
|
||||
var TaskManager = function(client) {
|
||||
this.client = client;
|
||||
};
|
||||
|
||||
TaskManager.prototype = {
|
||||
selectors: {
|
||||
element: '#task-manager',
|
||||
scrollElement: '#cards-view',
|
||||
newSheetButton: '#task-manager-new-sheet-button',
|
||||
newPrivateSheetButton: '#task-manager-new-private-sheet-button',
|
||||
cards: '#cards-view li',
|
||||
screenshot: '.screenshotView',
|
||||
icon: '.appIcon',
|
||||
close: '.close-button'
|
||||
},
|
||||
get element() {
|
||||
return this.client.helper.waitForElement(this.selectors.element);
|
||||
},
|
||||
|
||||
get scrollElement() {
|
||||
return this.client.helper.waitForElement(this.selectors.scrollElement);
|
||||
},
|
||||
|
||||
get cards() {
|
||||
return this.client.findElements(this.selectors.cards);
|
||||
},
|
||||
|
||||
get newSheetButton() {
|
||||
return this.client.helper.waitForElement(this.selectors.newSheetButton);
|
||||
},
|
||||
|
||||
get newPrivateSheetButton() {
|
||||
return this.client.helper.waitForElement(
|
||||
this.selectors.newPrivateSheetButton);
|
||||
},
|
||||
|
||||
show: function() {
|
||||
this.client.switchToFrame();
|
||||
this.client.executeAsyncScript(function() {
|
||||
var win = window.wrappedJSObject;
|
||||
win.addEventListener('cardviewshown', function wait() {
|
||||
win.removeEventListener('cardviewshown', wait);
|
||||
marionetteScriptFinished();
|
||||
});
|
||||
win.dispatchEvent(new CustomEvent('holdhome'));
|
||||
});
|
||||
},
|
||||
|
||||
hide: function() {
|
||||
this.client.switchToFrame();
|
||||
this.client.executeAsyncScript(function() {
|
||||
var win = window.wrappedJSObject;
|
||||
win.addEventListener('cardviewclosed', function wait() {
|
||||
win.removeEventListener('cardviewclosed', wait);
|
||||
marionetteScriptFinished();
|
||||
});
|
||||
win.dispatchEvent(new CustomEvent('home'));
|
||||
});
|
||||
},
|
||||
|
||||
waitUntilHidden: function() {
|
||||
this.client.switchToFrame();
|
||||
this.client.waitFor(function() {
|
||||
return this.client.findElement(this.selectors.element)
|
||||
.scriptWith(function(el) {
|
||||
return !el.classList.contains('active');
|
||||
});
|
||||
}.bind(this));
|
||||
},
|
||||
|
||||
getIconForCard: function(idx) {
|
||||
var card = this.cards[idx];
|
||||
this.client.waitFor(card.displayed.bind(card));
|
||||
var icon = card.findElement(this.selectors.icon);
|
||||
this.client.waitFor(icon.displayed.bind(icon));
|
||||
return icon;
|
||||
}
|
||||
};
|
||||
|
||||
module.exports = TaskManager;
|
||||
})(module);
|
||||
170
gaia/apps/system/test/marionette/lib/text_selection.js
Executable file
170
gaia/apps/system/test/marionette/lib/text_selection.js
Executable file
|
|
@ -0,0 +1,170 @@
|
|||
/* global Components */
|
||||
'use strict';
|
||||
|
||||
var Actions = require('marionette-client').Actions;
|
||||
function TextSelection(client) {
|
||||
this.client = client;
|
||||
this._activeContent = null;
|
||||
this.actions = new Actions(this.client);
|
||||
}
|
||||
|
||||
module.exports = TextSelection;
|
||||
|
||||
TextSelection.Selector = Object.freeze({
|
||||
textSelectionDialog: '.textselection-dialog',
|
||||
copy: '.textselection-dialog .textselection-dialog-copy',
|
||||
paste: '.textselection-dialog .textselection-dialog-paste',
|
||||
cut: '.textselection-dialog .textselection-dialog-cut',
|
||||
selectall: '.textselection-dialog .textselection-dialog-selectall'
|
||||
});
|
||||
|
||||
TextSelection.prototype = {
|
||||
get visibility() {
|
||||
this.client.switchToFrame();
|
||||
var isVisible = this.client.executeScript(function(query) {
|
||||
var element = document.querySelector(query);
|
||||
return element && element.classList.contains('visible');
|
||||
}, [TextSelection.Selector.textSelectionDialog]);
|
||||
this.client.apps.switchToApp(this._getDisplayedAppInfo().origin);
|
||||
return isVisible;
|
||||
|
||||
},
|
||||
|
||||
startCountVisibilityChanged: function() {
|
||||
this.client.switchToFrame();
|
||||
this.client.executeScript(function(query) {
|
||||
var element = document.querySelector(query);
|
||||
element.dataset.showSummary = 0;
|
||||
var bubbleInitActive = element.classList.contains('visible');
|
||||
element.addEventListener('DOMAttrModified',
|
||||
function DOMAttrModifiedListener(evt) {
|
||||
var active = element.classList.contains('active');
|
||||
if (bubbleInitActive !== active) {
|
||||
bubbleInitActive = active;
|
||||
element.dataset.showSummary =
|
||||
parseInt(element.dataset.showSummary, 10) + 1;
|
||||
}
|
||||
});
|
||||
}, [TextSelection.Selector.textSelectionDialog]);
|
||||
this.client.apps.switchToApp(this._getDisplayedAppInfo().origin);
|
||||
},
|
||||
|
||||
stopCountVisibilityChanged: function() {
|
||||
this.client.switchToFrame();
|
||||
var result = this.client.executeScript(function(query) {
|
||||
var element = document.querySelector(query);
|
||||
element.removeEventListener('DOMAttrModified',
|
||||
window.wrappedJSObject.DOMAttrModifiedListener);
|
||||
var showSummary = element.dataset.showSummary;
|
||||
delete element.dataset.showSummary;
|
||||
return parseInt(showSummary, 10);
|
||||
}, [TextSelection.Selector.textSelectionDialog]);
|
||||
this.client.apps.switchToApp(this._getDisplayedAppInfo().origin);
|
||||
return result;
|
||||
},
|
||||
|
||||
/**
|
||||
* Get dialog width from previous operation.
|
||||
*/
|
||||
get width() {
|
||||
// We need to make sure we're in system app before getting
|
||||
// dialog's location.
|
||||
this.client.switchToFrame();
|
||||
var size = this.client.helper.waitForElement(
|
||||
TextSelection.Selector.textSelectionDialog).size();
|
||||
this.client.apps.switchToApp(this._getDisplayedAppInfo().origin);
|
||||
return size.width;
|
||||
},
|
||||
|
||||
/**
|
||||
* Get dialog position from previous operation.
|
||||
*/
|
||||
get location() {
|
||||
this.client.switchToFrame();
|
||||
var location = this.client.helper.waitForElement(
|
||||
TextSelection.Selector.textSelectionDialog).location();
|
||||
this.client.apps.switchToApp(this._getDisplayedAppInfo().origin);
|
||||
return location;
|
||||
},
|
||||
|
||||
switchToCurrentApp: function() {
|
||||
this.client.apps.switchToApp(this._getDisplayedAppInfo().origin);
|
||||
},
|
||||
|
||||
/**
|
||||
* Get appWindow's id and origin of displayed app.
|
||||
* XXXXX: Since gecko is not ready yet, we need to simulate gecko dispatching
|
||||
* mozbrowsertextualmenu event to display textselection dialog.
|
||||
*
|
||||
* @return {displayApp}.
|
||||
*/
|
||||
_getDisplayedAppInfo: function() {
|
||||
// AppWindow information is stored in system app scope.
|
||||
var displayApp;
|
||||
this.client.waitFor(function() {
|
||||
displayApp = this.client.executeScript(function() {
|
||||
var app = window.wrappedJSObject.Service.query(
|
||||
'AppWindowManager.getActiveWindow');
|
||||
var frame = (app.browser) ? app.browser.element : app.frame.firstChild;
|
||||
return {
|
||||
appWindowId: frame.id,
|
||||
origin: app.origin
|
||||
};
|
||||
});
|
||||
return displayApp;
|
||||
}.bind(this));
|
||||
return displayApp;
|
||||
},
|
||||
|
||||
longPress: function(element) {
|
||||
// Get element's bounding rectangle. The coordinate is relative to
|
||||
// the top left corner of the window instead of the document.
|
||||
var rect = this.client.executeScript(function() {
|
||||
var rect = arguments[0].getBoundingClientRect();
|
||||
return {x: rect.x, y:rect.y, width: rect.width, height: rect.height};
|
||||
}, [element], null, 'system');
|
||||
|
||||
this.longPressByPosition(element, rect.x + rect.width / 2,
|
||||
rect.y + rect.height / 2);
|
||||
},
|
||||
|
||||
longPressByPosition: function(element, x, y) {
|
||||
this.client.executeScript(function() {
|
||||
var Ci = Components.interfaces;
|
||||
var utils = window.QueryInterface(Ci.nsIInterfaceRequestor)
|
||||
.getInterface(Ci.nsIDOMWindowUtils);
|
||||
utils.sendTouchEventToWindow('touchstart', [0],
|
||||
[arguments[0]], [arguments[1]],
|
||||
[1], [1], [0], [1], 1, 0);
|
||||
utils.sendMouseEventToWindow('mouselongtap', arguments[0], arguments[1],
|
||||
0, 1, 0);
|
||||
utils.sendTouchEventToWindow('touchend', [0],
|
||||
[arguments[0]], [arguments[1]],
|
||||
[1], [1], [0], [1], 1, 0);
|
||||
}, [x, y], null, 'system');
|
||||
},
|
||||
|
||||
pressCopy: function() {
|
||||
this.client.switchToFrame();
|
||||
this.client.helper.waitForElement(TextSelection.Selector.copy).tap();
|
||||
this.client.apps.switchToApp(this._getDisplayedAppInfo().origin);
|
||||
},
|
||||
|
||||
pressCut: function() {
|
||||
this.client.switchToFrame();
|
||||
this.client.helper.waitForElement(TextSelection.Selector.cut).tap();
|
||||
this.client.apps.switchToApp(this._getDisplayedAppInfo().origin);
|
||||
},
|
||||
|
||||
pressPaste: function() {
|
||||
this.client.switchToFrame();
|
||||
this.client.helper.waitForElement(TextSelection.Selector.paste).tap();
|
||||
this.client.apps.switchToApp(this._getDisplayedAppInfo().origin);
|
||||
},
|
||||
|
||||
pressSelectAll: function() {
|
||||
this.client.switchToFrame();
|
||||
this.client.helper.waitForElement(TextSelection.Selector.selectall).tap();
|
||||
this.client.apps.switchToApp(this._getDisplayedAppInfo().origin);
|
||||
}
|
||||
};
|
||||
149
gaia/apps/system/test/marionette/lib/utility_tray.js
Executable file
149
gaia/apps/system/test/marionette/lib/utility_tray.js
Executable file
|
|
@ -0,0 +1,149 @@
|
|||
'use strict';
|
||||
(function(module) {
|
||||
|
||||
var UtilityTray = function(client) {
|
||||
this.client = client;
|
||||
this.actions = client.loader.getActions();
|
||||
var width = client.executeScript(function() {
|
||||
return window.innerWidth;
|
||||
});
|
||||
this.halfWidth = width / 2;
|
||||
|
||||
var height = client.executeScript(function() {
|
||||
return window.innerHeight;
|
||||
});
|
||||
this.halfHeight = height / 2;
|
||||
|
||||
this.waitForLoaded();
|
||||
};
|
||||
|
||||
UtilityTray.prototype = {
|
||||
Selectors: {
|
||||
element: '#utility-tray',
|
||||
screen: '#screen.utility-tray',
|
||||
notifContainer: '#desktop-notifications-container',
|
||||
notification: '#desktop-notifications-container .notification',
|
||||
grippy: '#utility-tray-grippy',
|
||||
invisibleGrippy: '#tray-invisible-gripper',
|
||||
quickSettings: '#quick-settings-full-app',
|
||||
},
|
||||
|
||||
waitForLoaded: function() {
|
||||
return this.client.waitFor(function() {
|
||||
return this.client.executeScript(function() {
|
||||
var win = window.wrappedJSObject;
|
||||
return !!(win.UtilityTray &&
|
||||
win.UtilityTray.motion.el.scrollTopMax > 0);
|
||||
});
|
||||
}.bind(this));
|
||||
},
|
||||
|
||||
get visible() {
|
||||
return !!this.screenElement;
|
||||
},
|
||||
|
||||
get screenElement() {
|
||||
return this.client.findElement(this.Selectors.screen);
|
||||
},
|
||||
|
||||
get _motionState() {
|
||||
return this.client.executeScript(function() {
|
||||
return window.wrappedJSObject.UtilityTray.motion.state;
|
||||
});
|
||||
},
|
||||
|
||||
get shown() {
|
||||
return this.client.executeScript(function() {
|
||||
return window.wrappedJSObject.UtilityTray.shown;
|
||||
});
|
||||
},
|
||||
|
||||
get isAriaHidden() {
|
||||
return this.client.findElement(this.Selectors.element)
|
||||
.getAttribute('aria-hidden') === 'true';
|
||||
},
|
||||
|
||||
get quickSettings() {
|
||||
return this.client.helper.waitForElement(this.Selectors.quickSettings);
|
||||
},
|
||||
|
||||
get firstNotification() {
|
||||
return this.client.findElement(this.Selectors.notification);
|
||||
},
|
||||
|
||||
isOpened: function() {
|
||||
var element = this.client.findElement(this.Selectors.element);
|
||||
var rect = element.scriptWith(function(el) {
|
||||
return el.getBoundingClientRect();
|
||||
});
|
||||
var expectedTop = 0;
|
||||
return (rect.top === expectedTop) && this._motionState === 'open';
|
||||
},
|
||||
|
||||
waitForOpened: function() {
|
||||
this.client.waitFor(function() {
|
||||
return this.isOpened();
|
||||
}.bind(this));
|
||||
},
|
||||
|
||||
waitForClosed: function() {
|
||||
this.client.waitFor(function() {
|
||||
var element = this.client.findElement(this.Selectors.element);
|
||||
var rect = element.scriptWith(function(el) {
|
||||
return el.getBoundingClientRect();
|
||||
});
|
||||
var expectedTop = -rect.height;
|
||||
return (rect.top === expectedTop) && this._motionState === 'closed';
|
||||
}.bind(this));
|
||||
},
|
||||
|
||||
open: function() {
|
||||
this.client.executeAsyncScript(function() {
|
||||
var win = window.wrappedJSObject;
|
||||
win.addEventListener('utility-tray-overlayopened', function wait() {
|
||||
win.removeEventListener('utility-tray-overlayopened', wait);
|
||||
marionetteScriptFinished();
|
||||
});
|
||||
win.Service.request('UtilityTray:show', true);
|
||||
});
|
||||
},
|
||||
|
||||
close: function() {
|
||||
this.client.executeAsyncScript(function() {
|
||||
var win = window.wrappedJSObject;
|
||||
win.addEventListener('utility-tray-overlayclosed', function wait() {
|
||||
win.removeEventListener('utility-tray-overlayclosed', wait);
|
||||
marionetteScriptFinished();
|
||||
});
|
||||
win.Service.request('UtilityTray:hide');
|
||||
});
|
||||
},
|
||||
|
||||
swipeDown: function swipeDown() {
|
||||
var element = this.client.findElement(this.Selectors.invisibleGrippy);
|
||||
this.client.waitFor(function() {
|
||||
return element.displayed;
|
||||
});
|
||||
this.actions
|
||||
.flick(element, 10, 10, 10, this.halfHeight * 2, 500)
|
||||
.perform();
|
||||
|
||||
if (!this.isOpened()) {
|
||||
this.swipeDown();
|
||||
}
|
||||
},
|
||||
|
||||
swipeUp: function swipeUp() {
|
||||
var element = this.client.findElement(this.Selectors.grippy);
|
||||
this.client.waitFor(function() {
|
||||
return element.displayed;
|
||||
});
|
||||
this.actions
|
||||
.flick(element, 10, 10, 10, -(this.halfHeight * 2), 500)
|
||||
.perform();
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
module.exports = UtilityTray;
|
||||
})(module);
|
||||
132
gaia/apps/system/test/marionette/lib/video_player.js
Executable file
132
gaia/apps/system/test/marionette/lib/video_player.js
Executable file
|
|
@ -0,0 +1,132 @@
|
|||
'use strict';
|
||||
|
||||
function VideoPlayer(client) {
|
||||
this.client = client.scope({
|
||||
searchTimeout: 20000
|
||||
});
|
||||
this.chromeClient = client.scope({
|
||||
searchTimeout: 20000,
|
||||
context: 'chrome'
|
||||
});
|
||||
}
|
||||
|
||||
module.exports = VideoPlayer;
|
||||
|
||||
VideoPlayer.prototype = {
|
||||
client: null,
|
||||
|
||||
Selectors: Object.freeze({
|
||||
root: 'video',
|
||||
playButton: 'playButton',
|
||||
muteButton: 'muteButton',
|
||||
fullscreenButton: 'fullscreenButton'
|
||||
}),
|
||||
|
||||
get rootElement() {
|
||||
return this.client.helper.waitForElement(this.Selectors.root);
|
||||
},
|
||||
|
||||
get currentTimestamp() {
|
||||
return parseFloat(this.rootElement.getAttribute('currentTime'));
|
||||
},
|
||||
|
||||
isPlaying: function() {
|
||||
return this.rootElement.getAttribute('paused') !== 'true';
|
||||
},
|
||||
|
||||
isMuted: function() {
|
||||
return this.rootElement.getAttribute('muted') === 'true';
|
||||
},
|
||||
|
||||
isFullScreen: function() {
|
||||
return this.client.executeScript(function() {
|
||||
var fullScreenElement = document.mozFullScreenElement;
|
||||
var videoElement = document.querySelector('video');
|
||||
return fullScreenElement === videoElement;
|
||||
});
|
||||
},
|
||||
|
||||
waitForVideoLoaded: function() {
|
||||
this.client.waitFor(function() {
|
||||
return this.rootElement.getAttribute('readyState') === '4';
|
||||
}.bind(this));
|
||||
},
|
||||
|
||||
visibleControls: function() {
|
||||
var location = this._getLocation(this.Selectors.playButton);
|
||||
return location.x > 0;
|
||||
},
|
||||
|
||||
invokeControls: function() {
|
||||
if(this.visibleControls()) {
|
||||
this.rootElement.tap(0, 0);
|
||||
this.client.waitFor(function() {
|
||||
return !this.visibleControls();
|
||||
}.bind(this));
|
||||
}
|
||||
this.rootElement.tap();
|
||||
this.client.waitFor(function() {
|
||||
return this.visibleControls();
|
||||
}.bind(this));
|
||||
},
|
||||
|
||||
_tapVideoControl: function(name) {
|
||||
var location = this._getLocation(name);
|
||||
this.rootElement.tap(location.x, location.y);
|
||||
},
|
||||
|
||||
_getLocation: function(name) {
|
||||
var url = this.client.getUrl();
|
||||
return this.chromeClient.executeScript(function(url, name) {
|
||||
var Components = window.Components;
|
||||
var systemFrame = document.querySelector(
|
||||
'iframe[mozapp="app://system.gaiamobile.org/manifest.webapp"]');
|
||||
var systemDoc = systemFrame.contentWindow.document;
|
||||
var frame = systemDoc.querySelector('iframe[src="' + url + '"');
|
||||
var video = frame.contentWindow.document.querySelector('video');
|
||||
var a = Components.classes['@mozilla.org/inspector/dom-utils;1']
|
||||
.getService(Components.interfaces.inIDOMUtils)
|
||||
.getChildrenForNode(video, true);
|
||||
|
||||
var element = a[1].ownerDocument
|
||||
.getAnonymousElementByAttribute(a[1], 'class', name);
|
||||
var videoPosition = video.getBoundingClientRect();
|
||||
var elementPosition = element.getBoundingClientRect();
|
||||
var x = elementPosition.x - videoPosition.x;
|
||||
var y = elementPosition.y - videoPosition.y;
|
||||
return {x: x, y: y};
|
||||
}, [url, name]);
|
||||
},
|
||||
|
||||
tapPlay: function() {
|
||||
this._tapVideoControl(this.Selectors.playButton);
|
||||
this.client.waitFor(function() {
|
||||
return this.isPlaying();
|
||||
}.bind(this));
|
||||
},
|
||||
|
||||
tapPause: function() {
|
||||
this._tapVideoControl(this.Selectors.playButton);
|
||||
this.client.waitFor(function() {
|
||||
return !this.isPlaying();
|
||||
}.bind(this));
|
||||
},
|
||||
|
||||
tapMute: function() {
|
||||
this._tapVideoControl(this.Selectors.muteButton);
|
||||
this.client.waitFor(function() {
|
||||
return this.isMuted();
|
||||
}.bind(this));
|
||||
},
|
||||
|
||||
tapUnmute: function() {
|
||||
this._tapVideoControl(this.Selectors.muteButton);
|
||||
this.client.waitFor(function() {
|
||||
return !this.isMuted();
|
||||
}.bind(this));
|
||||
},
|
||||
|
||||
tapFullscreen: function() {
|
||||
this._tapVideoControl(this.Selectors.fullscreenButton);
|
||||
}
|
||||
};
|
||||
Loading…
Reference in a new issue