UNPKG

askui

Version:

Reliable, automated end-to-end-testing that depends on what is shown on your screen instead of the technology you are running on

138 lines (137 loc) 6.52 kB
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } return new (P || (P = Promise))(function (resolve, reject) { function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } step((generator = generator.apply(thisArg, _arguments || [])).next()); }); }; import WebSocket from 'ws'; import { logger } from '../../lib/logger'; import { UiControllerClientConnectionState } from '../ui-controller-client-connection-state'; import { LegacyControllerError } from './legacy-controller-error'; import { LegacyControllerNotConnectedError } from './legacy-controller-not-connected-error'; /** * Drives an Android device through the AskUI legacy UI Controller over its * WebSocket "runner protocol" (default `ws://127.0.0.1:6769`), implementing * {@link DeviceClient} so it is interchangeable with the desktop AgentOS gRPC * client and the direct-adb Android client. * * The controller must already be running in Android mode * (`AskUI-StartController ... -r android`). The controller serves one command at * a time, so requests are serialized. */ export class LegacyAndroidClient { constructor(args = {}) { var _a; this.connectionState = UiControllerClientConnectionState.NOT_CONNECTED; this.url = (_a = args.controllerUrl) !== null && _a !== void 0 ? _a : LegacyAndroidClient.DEFAULT_URL; } connect() { this.connectionState = UiControllerClientConnectionState.CONNECTING; return new Promise((resolve, reject) => { const ws = new WebSocket(this.url); const timeout = setTimeout(() => { ws.terminate(); reject(new LegacyControllerError(`Connection to the AskUI legacy UI Controller at "${this.url}" timed out. ` + 'Make sure it is running in Android mode (AskUI-StartController ... -r android).')); }, LegacyAndroidClient.CONNECT_TIMEOUT_IN_MS); ws.on('open', () => { clearTimeout(timeout); this.ws = ws; this.connectionState = UiControllerClientConnectionState.CONNECTED; logger.debug(`Connected to legacy UI Controller at ${this.url}`); resolve(this.connectionState); }); ws.on('message', (data) => this.onMessage(data)); ws.on('error', (error) => { clearTimeout(timeout); this.connectionState = UiControllerClientConnectionState.ERROR; reject(new LegacyControllerError(`Connection to the AskUI legacy UI Controller at "${this.url}" cannot be established. ` + 'Make sure it is running in Android mode (AskUI-StartController ... -r android). ' + `Cause: ${error.message}`)); }); }); } onMessage(data) { var _a; if (this.pending === undefined) { return; } const { resolve, reject, timeout } = this.pending; clearTimeout(timeout); this.pending = undefined; let response; try { response = JSON.parse(data.toString()); } catch (error) { reject(new LegacyControllerError(`Malformed response from the legacy UI Controller: ${error}`)); return; } if ((_a = response.data) === null || _a === void 0 ? void 0 : _a.error) { reject(new LegacyControllerError(response.data.error)); return; } resolve(response); } disconnect() { var _a; (_a = this.ws) === null || _a === void 0 ? void 0 : _a.close(); this.ws = undefined; this.connectionState = UiControllerClientConnectionState.NOT_CONNECTED; } sendAndReceive(request) { const { ws } = this; if (ws === undefined || this.connectionState !== UiControllerClientConnectionState.CONNECTED) { throw new LegacyControllerNotConnectedError(); } if (this.pending !== undefined) { throw new LegacyControllerError('A request to the legacy UI Controller is already in flight. ' + 'Requests must be serialized (check for a missing await).'); } return new Promise((resolve, reject) => { const timeout = setTimeout(() => { this.pending = undefined; reject(new LegacyControllerError(`Request "${request.msgName}" to the legacy UI Controller timed out.`)); }, LegacyAndroidClient.REQUEST_TIMEOUT_IN_MS); this.pending = { reject, resolve, timeout }; logger.debug(`Send to legacy UI Controller: ${request.msgName}`); ws.send(JSON.stringify(request)); }); } requestControl(controlCommand) { return __awaiter(this, void 0, void 0, function* () { yield this.sendAndReceive({ controlCommand: controlCommand.toJson(), msgName: 'CONTROL_REQUEST', }); }); } requestScreenshot() { return __awaiter(this, void 0, void 0, function* () { const response = yield this.sendAndReceive({ msgName: 'CAPTURE_SCREENSHOT_REQUEST' }); if (response.data.image === undefined) { throw new LegacyControllerError('The legacy UI Controller returned no screenshot.'); } return response.data.image; }); } getStartingArguments() { return __awaiter(this, void 0, void 0, function* () { var _a; const response = yield this.sendAndReceive({ msgName: 'GET_STARTING_ARGUMENTS_REQUEST' }); return (_a = response.data.arguments) !== null && _a !== void 0 ? _a : {}; }); } // eslint-disable-next-line class-methods-use-this setActiveDisplay() { return __awaiter(this, void 0, void 0, function* () { // The legacy controller selects its display/device when it is launched. }); } } LegacyAndroidClient.DEFAULT_URL = 'ws://127.0.0.1:6769'; LegacyAndroidClient.REQUEST_TIMEOUT_IN_MS = 30000; LegacyAndroidClient.CONNECT_TIMEOUT_IN_MS = 10000;