appium-flutter-driver
Version:
Appium Flutter driver
160 lines • 7.51 kB
JavaScript
;
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.OBSERVATORY_URL_PATTERN = void 0;
exports.connectSocket = connectSocket;
exports.executeGetIsolateCommand = executeGetIsolateCommand;
exports.executeGetVMCommand = executeGetVMCommand;
exports.executeElementCommand = executeElementCommand;
exports.extractObservatoryUrl = extractObservatoryUrl;
const node_url_1 = require("node:url");
const lodash_1 = __importDefault(require("lodash"));
const isolate_socket_1 = require("./isolate_socket");
const base64url_1 = require("./base64url");
const asyncbox_1 = require("asyncbox");
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=/'
exports.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:/=_\\-.\\[\\]]+)`);
const moduleCheckIntervalCount = 30;
const moduleCheckIntervalMs = 500;
// SOCKETS
async function connectSocket(dartObservatoryURL, caps) {
const isolateId = caps.isolateId;
this.log.debug(`Establishing a connection to the Dart Observatory`);
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) => {
this.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`, () => {
this.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 = () => {
this.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) {
this.log.errorWithException(new Error(JSON.stringify(e)));
}
};
this.log.info(`Connecting to Dart Observatory: ${dartObservatoryURL}`);
if (isolateId) {
this.log.info(`Listing the given isolate id: ${isolateId}`);
socket.isolateId = isolateId;
}
else {
const vm = (await socket.call(`getVM`));
this.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) {
this.log.error(`Cannot get Dart main isolate info`);
removeListenerAndResolve(null);
socket.close();
return;
}
// e.g. 'isolates/2978358234363215', '2978358234363215'
socket.isolateId = mainIsolateData.id;
}
// It could take time to load the expected module.
try {
await (0, asyncbox_1.retryInterval)(moduleCheckIntervalCount, moduleCheckIntervalMs, async () => {
const isolate = (await socket.call(`getIsolate`, {
isolateId: `${socket.isolateId}`,
}));
if (!isolate) {
throw new Error(`Cannot get main Dart Isolate`);
}
if (!Array.isArray(isolate.extensionRPCs)) {
throw new Error(`Cannot get Dart extensionRPCs from isolate ${JSON.stringify(isolate)}`);
}
if (isolate.extensionRPCs.indexOf(`ext.flutter.driver`) < 0) {
throw new Error(`"ext.flutter.driver" is not found in "extensionRPCs" ${JSON.stringify(isolate.extensionRPCs)}`);
}
});
}
catch (e) {
this.log.error(e.message);
removeListenerAndResolve(null);
return;
}
removeListenerAndResolve(socket);
};
socket.on(`open`, onOpenListener);
});
const connectedSocket = await connectedPromise;
if (connectedSocket) {
return connectedSocket;
}
throw new Error(`Cannot connect to the Dart Observatory URL ${dartObservatoryURL}. ` +
`Check the server log for more details`);
}
async function executeGetIsolateCommand(isolateId) {
this.log.debug(`>>> getIsolate`);
const isolate = await this.socket.call(`getIsolate`, {
isolateId: `${isolateId}`,
});
this.log.debug(`<<< ${lodash_1.default.truncate(JSON.stringify(isolate), { length: truncateLength })}`);
return isolate;
}
async function executeGetVMCommand() {
this.log.debug(`>>> getVM`);
const vm = (await this.socket.call(`getVM`));
this.log.debug(`<<< ${lodash_1.default.truncate(JSON.stringify(vm), { length: truncateLength })}`);
return vm;
}
async function executeElementCommand(command, elementBase64, extraArgs = {}) {
const elementObject = elementBase64 ? JSON.parse((0, base64url_1.decode)(elementBase64)) : {};
const serializedCommand = { command, ...elementObject, ...extraArgs };
this.log.debug(`>>> ${JSON.stringify(serializedCommand)}`);
const data = await this.socket.executeSocketCommand(serializedCommand);
this.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;
}
function extractObservatoryUrl(logEntry) {
const match = logEntry.message.match(exports.OBSERVATORY_URL_PATTERN);
if (!match) {
return null;
}
try {
const result = new node_url_1.URL(match[2]);
result.protocol = `ws`;
result.pathname += `ws`;
return result;
}
catch {
return null;
}
}
//# sourceMappingURL=observatory.js.map