Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: "mobile: getContexts" on Android #662

Merged
merged 8 commits into from
Sep 22, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
56 changes: 56 additions & 0 deletions lib/commands/context.js
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,62 @@ commands.setContext = async function setContext (name) {
this.curContext = name;
};

/**
* @typedef {Object} WebviewsMapping
* @property {string} proc The name of the Devtools Unix socket
* @property {string} webview The web view alias. Looks like `WEBVIEW_`
* prefix plus PID or package name
* @property {?Object} info Webview information as it is retrieved by
* /json/version CDP endpoint
* @property {?Array<Object>} pages Webview pages list as it is retrieved by
* /json/list CDP endpoint
* @propery {?string} webviewName An actual webview name for switching context.
* This value becomes null when failing to find a PID for a webview.
*
* The following json demonstrates the example of WebviewsMapping object.
* Note that `description` in `page` can be an empty string most likely when it comes to Mobile Chrome)
* {
* "proc": "@webview_devtools_remote_22138",
* "webview": "WEBVIEW_22138",
* "info": {
* "Android-Package": "io.appium.settings",
* "Browser": "Chrome/74.0.3729.185",
* "Protocol-Version": "1.3",
* "User-Agent": "Mozilla/5.0 (Linux; Android 10; Android SDK built for x86 Build/QSR1.190920.001; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/74.0.3729.185 Mobile Safari/537.36",
* "V8-Version": "7.4.288.28",
* "WebKit-Version": "537.36 (@22955682f94ce09336197bfb8dffea991fa32f0d)",
* "webSocketDebuggerUrl": "ws://127.0.0.1:10900/devtools/browser"
* },
* "pages": [
* {
* "description": "{\"attached\":true,\"empty\":false,\"height\":1458,\"screenX\":0,\"screenY\":336,\"visible\":true,\"width\":1080}",
* "devtoolsFrontendUrl": "http://chrome-devtools-frontend.appspot.com/serve_rev/@22955682f94ce09336197bfb8dffea991fa32f0d/inspector.html?ws=127.0.0.1:10900/devtools/page/27325CC50B600D31B233F45E09487B1F",
* "id": "27325CC50B600D31B233F45E09487B1F",
* "title": "Releases · appium/appium · GitHub",
* "type": "page",
* "url": "https://github.com/appium/appium/releases",
* "webSocketDebuggerUrl": "ws://127.0.0.1:10900/devtools/page/27325CC50B600D31B233F45E09487B1F"
* }
* ],
* "webviewName": "WEBVIEW_com.io.appium.setting"
* }
*/

/**
* Returns a webviewsMapping based on CDP endpoints
*
* @return {Array<WebviewsMapping>} webviewsMapping
*/
commands.mobileGetContexts = async function mobileGetContexts () {
mykola-mokhnach marked this conversation as resolved.
Show resolved Hide resolved
const opts = {
androidDeviceSocket: this.opts.androidDeviceSocket,
ensureWebviewsHavePages: true,
webviewDevtoolsPort: this.opts.webviewDevtoolsPort,
enableWebviewDetailsCollection: true
};
return await webviewHelpers.getWebViewsMapping(this.adb, opts);
};

helpers.switchContext = async function switchContext (name) {
// We have some options when it comes to webviews. If we want a
// Chromedriver webview, we can only control one at a time.
Expand Down
2 changes: 2 additions & 0 deletions lib/commands/execute.js
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,8 @@ extensions.executeMobile = async function executeMobile (mobileCommand, opts = {

startService: 'mobileStartService',
stopService: 'mobileStopService',

getContexts: 'mobileGetContexts',
};

if (!_.has(mobileCommandsMapping, mobileCommand)) {
Expand Down
71 changes: 54 additions & 17 deletions lib/webview-helpers.js
Original file line number Diff line number Diff line change
Expand Up @@ -289,6 +289,41 @@ helpers.procFromWebview = async function procFromWebview (adb, webview) {
return pkg;
};

/**
* Retrieves webview names for getContexts
*
* @param {object} adb - an ADB instance
* @param {GetWebviewOpts} opts See note on getWebViewsMapping
*
* @return {Array.<string>} - a list of webview names
*/
helpers.getWebviews = async function getWebviews (adb, {
androidDeviceSocket = null,
ensureWebviewsHavePages = true,
webviewDevtoolsPort = null,
enableWebviewDetailsCollection = null,
} = {}) {
const webviewsMapping = await this.getWebViewsMapping(adb, {
androidDeviceSocket,
ensureWebviewsHavePages,
webviewDevtoolsPort,
enableWebviewDetailsCollection
});
const result = [];
for (const {webview, pages, proc, webviewName} of webviewsMapping) {
if (ensureWebviewsHavePages && pages?.length === 0) {
logger.info(`Skipping the webview '${webview}' at '${proc}' ` +
`since it has reported having zero pages`);
continue;
}
if (webviewName) {
result.push(webviewName);
}
}
logger.debug(`Found ${util.pluralize('webview', result.length, true)}: ${JSON.stringify(result)}`);
return result;
};

/**
* @typedef {Object} GetWebviewsOpts
* @property {string} androidDeviceSocket [null] - device socket name
Expand All @@ -300,9 +335,19 @@ helpers.procFromWebview = async function procFromWebview (adb, webview) {
* web view details and send them to Chromedriver constructor, so it could
* select a binary more precisely based on this info.
*/

/**
* @typedef {Object} WebviewsMapping
* @property {string} proc See note on WebviewProps
* @property {string} webview See note on WebviewProps
* @property {?Object} info See note on WebviewProps
* @property {?Array<Object>} pages See note on WebviewProps
* @propery {?string} webviewName An actual webview name for switching context
*/

/**
* Get a list of available webviews by introspecting processes with adb, where
* webviews are listed. It's possible to pass in a 'deviceSocket' arg, which
* Get a list of available webviews mapping by introspecting processes with adb,
* where webviews are listed. It's possible to pass in a 'deviceSocket' arg, which
* limits the webview possibilities to the one running on the Chromium devtools
* socket we're interested in (see note on webviewsFromProcs). We can also
* direct this method to verify whether a particular webview process actually
Expand All @@ -316,9 +361,9 @@ helpers.procFromWebview = async function procFromWebview (adb, webview) {
* @param {object} adb - an ADB instance
* @param {GetWebviewOpts} opts
*
* @return {Array.<string>} - a list of webview names
* @return {Array<WebviewsMapping>} webviewsMapping
*/
helpers.getWebviews = async function getWebviews (adb, {
helpers.getWebViewsMapping = async function getWebViewsMapping (adb, {
androidDeviceSocket = null,
ensureWebviewsHavePages = true,
webviewDevtoolsPort = null,
Expand All @@ -333,16 +378,9 @@ helpers.getWebviews = async function getWebviews (adb, {
webviewDevtoolsPort,
});

// webviewProcs contains procs, which we only care about for ensuring
// presence of pages above, so we can now discard them and rely on just the
// webview names
const result = [];
for (const {webview, pages, info, proc} of webviewsMapping) {
if (ensureWebviewsHavePages && pages?.length === 0) {
logger.info(`Skipping the webview '${webview}' at '${proc}' ` +
`since it has reported having zero pages`);
continue;
}
for (const webviewMapping of webviewsMapping) {
const {webview, info} = webviewMapping;
webviewMapping.webviewName = null;

let wvName = webview;
if (!androidDeviceSocket) {
Expand All @@ -358,16 +396,15 @@ helpers.getWebviews = async function getWebviews (adb, {
}
}

result.push(wvName);
webviewMapping.webviewName = wvName;
const key = toDetailsCacheKey(adb, wvName);
if (info) {
WEBVIEWS_DETAILS_CACHE.set(key, { info });
} else if (WEBVIEWS_DETAILS_CACHE.has(key)) {
WEBVIEWS_DETAILS_CACHE.del(key);
}
}
logger.debug(`Found ${util.pluralize('webview', result.length, true)}: ${JSON.stringify(result)}`);
return result;
return webviewsMapping;
};

/**
Expand Down