UNPKG

appium-flutter-driver

Version:
198 lines 10.4 kB
"use strict"; 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