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

231 lines (230 loc) 11.4 kB
"use strict"; 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()); }); }; Object.defineProperty(exports, "__esModule", { value: true }); exports.ExecutionRuntime = void 0; const ui_control_commands_1 = require("../core/ui-control-commands"); const custom_element_1 = require("../core/model/custom-element"); const repeat_error_1 = require("./repeat-error"); const misc_1 = require("./misc"); const control_command_error_1 = require("./control-command-error"); const annotation_1 = require("../core/annotation/annotation"); const logger_1 = require("../lib/logger"); const base_64_image_1 = require("../utils/base_64_image/base-64-image"); class ExecutionRuntime { constructor(uiControllerClient, inferenceClient, stepReporter, retryStrategy) { this.uiControllerClient = uiControllerClient; this.inferenceClient = inferenceClient; this.stepReporter = stepReporter; this.retryStrategy = retryStrategy; this.EXEC_REPETITION_COUNT = 25; } connect() { return __awaiter(this, void 0, void 0, function* () { return this.uiControllerClient.connect(); }); } disconnect() { this.uiControllerClient.disconnect(); } startVideoRecording() { return __awaiter(this, void 0, void 0, function* () { yield this.uiControllerClient.startVideoRecording(); }); } stopVideoRecording() { return __awaiter(this, void 0, void 0, function* () { yield this.uiControllerClient.stopVideoRecording(); }); } readVideoRecording() { return __awaiter(this, void 0, void 0, function* () { const response = yield this.uiControllerClient.readVideoRecording(); return response.data.video; }); } requestControl(controlCommand) { return __awaiter(this, void 0, void 0, function* () { yield this.uiControllerClient.requestControl(controlCommand); }); } executeInstruction(instruction, modelComposition) { return __awaiter(this, void 0, void 0, function* () { var _a, _b, _c; const controlCommand = yield this.predictCommandWithRetry(instruction, modelComposition); if (controlCommand.code === ui_control_commands_1.ControlCommandCode.OK) { return this.requestControl(controlCommand); } if (controlCommand.tryToRepeat) { yield this.requestControl(controlCommand); return this.executeCommandRepeatedly(instruction, modelComposition); } throw new control_command_error_1.ControlCommandError((_c = (_b = (_a = controlCommand.actions) === null || _a === void 0 ? void 0 : _a[0]) === null || _b === void 0 ? void 0 : _b.text) !== null && _c !== void 0 ? _c : ''); }); } executeCommandRepeatedly(instruction, modelComposition) { return __awaiter(this, void 0, void 0, function* () { var _a, _b, _c; /* eslint-disable no-await-in-loop */ for (let repeatCount = this.EXEC_REPETITION_COUNT; repeatCount >= 0; repeatCount -= 1) { if (repeatCount === 0) { throw new repeat_error_1.RepeatError(`Max. number (${this.EXEC_REPETITION_COUNT}) of repetitions of command executions ` + 'from a single test step reached'); } logger_1.logger.debug('Repeat command execution....'); const controlCommand = yield this.predictCommandWithRetry(instruction, modelComposition); if (controlCommand.code === ui_control_commands_1.ControlCommandCode.OK) { break; } if (controlCommand.tryToRepeat) { yield this.requestControl(controlCommand); } else { throw new control_command_error_1.ControlCommandError((_c = (_b = (_a = controlCommand.actions) === null || _a === void 0 ? void 0 : _a[0]) === null || _b === void 0 ? void 0 : _b.text) !== null && _c !== void 0 ? _c : ''); } } /* eslint-enable no-await-in-loop */ }); } /** * Command prediction may fail, e.g., due to application still loading * --> retry with linear back-off */ /* eslint-disable-next-line consistent-return */ predictCommandWithRetry(instruction, modelComposition) { return __awaiter(this, void 0, void 0, function* () { var _a, _b, _c; let command = yield this.predictCommand(instruction, modelComposition); /* eslint-disable no-await-in-loop */ for (let k = 0; k < this.retryStrategy.retryCount; k += 1) { if (command.code === ui_control_commands_1.ControlCommandCode.OK) { return command; } const msUntilRetry = this.retryStrategy.getDelay(k + 1); logger_1.logger.debug(`Wait ${msUntilRetry} and retry predicting command...`); yield (0, misc_1.delay)(msUntilRetry); command = yield this.predictCommand(instruction, modelComposition, new control_command_error_1.ControlCommandError((_c = (_b = (_a = command.actions) === null || _a === void 0 ? void 0 : _a[0]) === null || _b === void 0 ? void 0 : _b.text) !== null && _c !== void 0 ? _c : '')); } /* eslint-enable no-await-in-loop */ return command; }); } isImageRequiredByConfig() { return this.stepReporter.config.withScreenshots === 'begin' || this.stepReporter.config.withScreenshots === 'always'; } isImageRequired(instruction) { return __awaiter(this, void 0, void 0, function* () { if (this.isImageRequiredByConfig()) return Promise.resolve(true); return this.inferenceClient.isImageRequired(instruction); }); } isAnnotationRequired() { return this.stepReporter.config.withDetectedElements === 'begin' || this.stepReporter.config.withDetectedElements === 'always'; } getScreenshot() { return __awaiter(this, void 0, void 0, function* () { const requestScreenshotResponse = yield this.uiControllerClient.requestScreenshot(); return requestScreenshotResponse.data.image; }); } buildSnapshot(instruction) { return __awaiter(this, void 0, void 0, function* () { const createdAt = new Date(); const screenshot = (yield this.isImageRequired(instruction)) ? yield this.getScreenshot() : undefined; const annotation = this.isAnnotationRequired() ? yield this.annotateImage() : undefined; return { createdAt, detectedElements: annotation === null || annotation === void 0 ? void 0 : annotation.detected_elements, screenshot, }; }); } predictCommand(instruction, modelComposition, retryError) { return __awaiter(this, void 0, void 0, function* () { const snapshot = yield this.buildSnapshot(instruction.value); if (retryError !== undefined) this.stepReporter.onStepRetry(snapshot, retryError); else this.stepReporter.onStepBegin(snapshot); const controlCommand = yield this.inferenceClient.predictControlCommand(instruction.value, modelComposition, instruction.customElements, snapshot.screenshot); if (instruction.secretText !== undefined) { controlCommand.setTextToBeTyped(instruction.secretText); } return controlCommand; }); } annotateInteractively() { return __awaiter(this, void 0, void 0, function* () { const annotationResponse = yield this.annotateImage(); yield this.uiControllerClient.annotateInteractively(annotationResponse.detected_elements, annotationResponse.image); }); } takeScreenshotIfImageisNotProvided(imagePath) { return __awaiter(this, void 0, void 0, function* () { let base64Image = ''; if (imagePath !== undefined) { base64Image = (yield base_64_image_1.Base64Image.fromPath(imagePath)).toString(); } if (imagePath === undefined) { const screenshotResponse = yield this.uiControllerClient.requestScreenshot(); base64Image = screenshotResponse.data.image; } return base64Image; }); } getStartingArguments() { return __awaiter(this, void 0, void 0, function* () { const startingArgumentsResponse = yield this.uiControllerClient.getStartingArguments(); return startingArgumentsResponse.data.arguments; }); } getDetectedElements(instruction, customElementJson) { return __awaiter(this, void 0, void 0, function* () { let customElements = []; const base64Image = yield this.takeScreenshotIfImageisNotProvided(); if (customElementJson !== undefined) { customElements = yield custom_element_1.CustomElement.fromJsonListWithImagePathOrImage(customElementJson); } return this.inferenceClient.getDetectedElements(instruction, base64Image, customElements); }); } annotateImage(imagePath, customElementJson, elements) { return __awaiter(this, void 0, void 0, function* () { let customElements = []; const base64Image = yield this.takeScreenshotIfImageisNotProvided(imagePath); if (elements !== undefined) { if (elements.length === 0) { logger_1.logger.warn("Parameter 'elements' is an empty list."); } return new annotation_1.Annotation(base64Image, elements); } if (customElementJson !== undefined) { customElements = yield custom_element_1.CustomElement.fromJsonListWithImagePathOrImage(customElementJson); } return this.inferenceClient.predictImageAnnotation(base64Image, customElements); }); } predictVQA(prompt, config) { return __awaiter(this, void 0, void 0, function* () { const base64Image = yield this.takeScreenshotIfImageisNotProvided(); return this.inferenceClient.predictVQAAnswer(prompt, base64Image, config); }); } predictActResponse(params) { return __awaiter(this, void 0, void 0, function* () { return this.inferenceClient.predictActResponse(params); }); } } exports.ExecutionRuntime = ExecutionRuntime;