appium-flutter-driver
Version:
198 lines • 10.4 kB
JavaScript
;
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.fetchObservatoryUrl = exports.executeElementCommand = exports.executeGetVMCommand = exports.executeGetIsolateCommand = exports.connectSocket = void 0;
const url_1 = require("url");
const lodash_1 = __importDefault(require("lodash"));
const logger_1 = require("../logger");
const isolate_socket_1 = require("./isolate_socket");
const base64url_1 = require("./base64url");
const bluebird_1 = __importDefault(require("bluebird"));
const platform_1 = require("../platform");
const truncateLength = 500;
// https://github.com/flutter/flutter/blob/f90b019c68edf4541a4c8273865a2b40c2c01eb3/dev/devicelab/lib/framework/runner.dart#L183
// e.g. 'Observatory listening on http://127.0.0.1:52817/_w_SwaKs9-g=/'
// https://github.com/flutter/flutter/blob/52ae102f182afaa0524d0d01d21b2d86d15a11dc/packages/flutter_tools/lib/src/resident_runner.dart#L1386-L1389
// e.g. 'An Observatory debugger and profiler on ${device.device.name} is available at: http://127.0.0.1:52817/_w_SwaKs9-g=/'
const OBSERVATORY_URL_PATTERN = new RegExp(`(Observatory listening on |` +
`An Observatory debugger and profiler on\\s.+\\sis available at: |` +
`The Dart VM service is listening on )` +
`((http|//)[a-zA-Z0-9:/=_\\-.\\[\\]]+)`);
// SOCKETS
const connectSocket = async (getObservatoryWsUri, flutterDriver, driver, caps) => {
const retryBackoff = caps.retryBackoffTime || 3000;
const maxRetryCount = caps.maxRetryCount || 10;
const isolateId = caps.isolateId;
let retryCount = 0;
let urlFetchError;
let dartObservatoryURL;
logger_1.log.debug(`Establishing a connection to the Dart Observatory. ` +
`Will retry ${maxRetryCount} times with ${retryBackoff}ms delay between retries. ` +
`These values could be customized by changing 'maxRetryCount' and 'retryBackoffTime' capabilities.`);
while (retryCount < maxRetryCount) {
if (retryCount > 0 && retryBackoff > 0) {
logger_1.log.info(`Waiting ${retryBackoff}ms before retrying`);
await bluebird_1.default.delay(retryBackoff);
}
logger_1.log.info(`Attempt #${(retryCount + 1)} of ${maxRetryCount}`);
// Every attempt gets the latest observatory url
try {
dartObservatoryURL = await getObservatoryWsUri(flutterDriver, driver, caps);
urlFetchError = undefined;
}
catch (e) {
urlFetchError = e;
logger_1.log.debug(`Got an error while finding an observatory url. Original error: ${e.message}`);
}
if (!urlFetchError) {
const connectedPromise = new Promise((resolve) => {
const socket = new isolate_socket_1.IsolateSocket(dartObservatoryURL);
const removeListenerAndResolve = (r) => {
socket.removeListener(`error`, onErrorListener);
socket.removeListener(`timeout`, onTimeoutListener);
socket.removeListener(`open`, onOpenListener);
resolve(r);
};
// Add an 'error' event handler for the client socket
const onErrorListener = (ex) => {
logger_1.log.error(`Connection to ${dartObservatoryURL} got an error: ${ex.message}`);
removeListenerAndResolve(null);
};
socket.on(`error`, onErrorListener);
// Add a 'close' event handler for the client socket
socket.on(`close`, () => {
logger_1.log.info(`Connection to ${dartObservatoryURL} closed`);
// @todo do we need to set this.socket = null?
});
// Add a 'timeout' event handler for the client socket
const onTimeoutListener = () => {
logger_1.log.error(`Connection to ${dartObservatoryURL} timed out`);
removeListenerAndResolve(null);
};
socket.on(`timeout`, onTimeoutListener);
const onOpenListener = async () => {
const originalSocketCall = socket.call;
socket.call = async (...args) => {
try {
// `await` is needed so that rejected promise will be thrown and caught
return await originalSocketCall.apply(socket, args);
}
catch (e) {
logger_1.log.errorAndThrow(JSON.stringify(e));
}
};
logger_1.log.info(`Connecting to Dart Observatory: ${dartObservatoryURL}`);
if (isolateId) {
logger_1.log.info(`Listing the given isolate id: ${isolateId}`);
socket.isolateId = isolateId;
}
else {
const vm = await socket.call(`getVM`);
logger_1.log.info(`Listing all isolates: ${JSON.stringify(vm.isolates)}`);
// To accept 'main.dart:main()' and 'main'
const mainIsolateData = vm.isolates.find((e) => e.name.includes(`main`));
if (!mainIsolateData) {
logger_1.log.error(`Cannot get Dart main isolate info`);
removeListenerAndResolve(null);
socket.close();
return;
}
// e.g. 'isolates/2978358234363215', '2978358234363215'
socket.isolateId = mainIsolateData.id;
}
// @todo check extension and do health check
const isolate = await socket.call(`getIsolate`, {
isolateId: `${socket.isolateId}`,
});
if (!isolate) {
logger_1.log.error(`Cannot get main Dart Isolate`);
removeListenerAndResolve(null);
return;
}
if (!Array.isArray(isolate.extensionRPCs)) {
logger_1.log.error(`Cannot get Dart extensionRPCs from isolate ${JSON.stringify(isolate)}`);
removeListenerAndResolve(null);
return;
}
if (isolate.extensionRPCs.indexOf(`ext.flutter.driver`) < 0) {
logger_1.log.error(`"ext.flutter.driver" is not found in "extensionRPCs" ${JSON.stringify(isolate.extensionRPCs)}`);
removeListenerAndResolve(null);
return;
}
removeListenerAndResolve(socket);
};
socket.on(`open`, onOpenListener);
});
const connectedSocket = await connectedPromise;
if (connectedSocket) {
return connectedSocket;
}
}
// re-create the port forward
switch (lodash_1.default.toLower(caps.platformName)) {
case platform_1.PLATFORM.IOS:
flutterDriver.localServer?.close();
break;
case platform_1.PLATFORM.ANDROID:
if (flutterDriver.portForwardLocalPort) {
await driver.adb.removePortForward(flutterDriver.portForwardLocalPort);
}
break;
}
retryCount++;
}
throw new Error(urlFetchError
? (`Cannot determine the Dart Observatory URL after ${maxRetryCount} retries. ` +
`Original error: ${urlFetchError.message}`)
: (`Cannot connect to the Dart Observatory URL ${dartObservatoryURL} after ` +
`${maxRetryCount} retries. Check the server log for more details`));
};
exports.connectSocket = connectSocket;
const executeGetIsolateCommand = async function (isolateId) {
logger_1.log.debug(`>>> getIsolate`);
const isolate = await this.socket.call(`getIsolate`, { isolateId: `${isolateId}` });
logger_1.log.debug(`<<< ${lodash_1.default.truncate(JSON.stringify(isolate), { 'length': truncateLength })}`);
return isolate;
};
exports.executeGetIsolateCommand = executeGetIsolateCommand;
const executeGetVMCommand = async function () {
logger_1.log.debug(`>>> getVM`);
const vm = await this.socket.call(`getVM`);
logger_1.log.debug(`<<< ${lodash_1.default.truncate(JSON.stringify(vm), { 'length': truncateLength })}`);
return vm;
};
exports.executeGetVMCommand = executeGetVMCommand;
const executeElementCommand = async function (command, elementBase64, extraArgs = {}) {
const elementObject = elementBase64 ? JSON.parse((0, base64url_1.decode)(elementBase64)) : {};
const serializedCommand = { command, ...elementObject, ...extraArgs };
logger_1.log.debug(`>>> ${JSON.stringify(serializedCommand)}`);
const data = await this.socket.executeSocketCommand(serializedCommand);
logger_1.log.debug(`<<< ${JSON.stringify(data)} | previous command ${command}`);
if (data.isError) {
throw new Error(`Cannot execute command ${command}, server response ${JSON.stringify(data, null, 2)}`);
}
return data.response;
};
exports.executeElementCommand = executeElementCommand;
const fetchObservatoryUrl = (deviceLogs) => {
let dartObservatoryURL;
for (const line of deviceLogs.map((e) => e.message).reverse()) {
const match = line.match(OBSERVATORY_URL_PATTERN);
if (match) {
dartObservatoryURL = new url_1.URL(match[2]);
break;
}
}
if (!dartObservatoryURL) {
throw new Error(`No observatory URL matching to '${OBSERVATORY_URL_PATTERN}' was found in the device log. ` +
`Please make sure the application under test is configured properly according to ` +
`https://github.com/appium-userland/appium-flutter-driver#usage and that it does not crash on startup.`);
}
dartObservatoryURL.protocol = `ws`;
dartObservatoryURL.pathname += `ws`;
return dartObservatoryURL;
};
exports.fetchObservatoryUrl = fetchObservatoryUrl;
//# sourceMappingURL=observatory.js.map