UNPKG

@wix-pilot/core

Version:

A flexible plugin that drives your tests with human-written commands, enhanced by the power of large language models (LLMs)

166 lines (165 loc) 7.13 kB
"use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.Pilot = void 0; const PilotError_1 = require("./errors/PilotError"); const StepPerformer_1 = require("./performers/step-performer/StepPerformer"); const CacheHandler_1 = require("./common/cacheHandler/CacheHandler"); const AutoPerformer_1 = require("./performers/auto-performer/AutoPerformer"); const AutoPerformerPromptCreator_1 = require("./performers/auto-performer/AutoPerformerPromptCreator"); const StepPerformerPromptCreator_1 = require("./performers/step-performer/StepPerformerPromptCreator"); const CodeEvaluator_1 = require("./common/CodeEvaluator"); const SnapshotComparator_1 = require("./common/snapshot/comparator/SnapshotComparator"); const SnapshotManager_1 = require("./common/snapshot/SnapshotManager"); const ScreenCapturer_1 = require("./common/snapshot/ScreenCapturer"); const downscaleImage_1 = __importDefault(require("./common/snapshot/downscaleImage")); const logger_1 = __importDefault(require("./common/logger")); /** * The main Pilot class that provides AI-assisted testing capabilities for a given underlying testing framework. * Enables writing tests in natural language that are translated into precise testing actions. * * Usage: * ```typescript * const pilot = new Pilot({ * frameworkDriver: new PuppeteerFrameworkDriver(), * promptHandler: new OpenAIHandler({ apiKey: process.env.OPENAI_API_KEY }) * }); * * pilot.start(); * await pilot.perform('Navigate to the login page'); * await pilot.perform('Enter "user@example.com" in the email field'); * pilot.end(); * ``` */ class Pilot { snapshotManager; previousSteps = []; stepPerformerPromptCreator; stepPerformer; cacheHandler; running = false; autoPerformer; screenCapturer; snapshotComparator; constructor(config) { // Configure logger delegate if provided if (config.loggerDelegate) { logger_1.default.setDelegate(config.loggerDelegate); } this.snapshotComparator = new SnapshotComparator_1.SnapshotComparator(); this.snapshotManager = new SnapshotManager_1.SnapshotManager(config.frameworkDriver, this.snapshotComparator, downscaleImage_1.default); this.cacheHandler = new CacheHandler_1.CacheHandler(this.snapshotComparator, config.options?.cacheOptions); this.stepPerformerPromptCreator = new StepPerformerPromptCreator_1.StepPerformerPromptCreator(config.frameworkDriver.apiCatalog); this.screenCapturer = new ScreenCapturer_1.ScreenCapturer(this.snapshotManager, config.promptHandler); this.stepPerformer = new StepPerformer_1.StepPerformer(config.frameworkDriver.apiCatalog.context, this.stepPerformerPromptCreator, new CodeEvaluator_1.CodeEvaluator(), config.promptHandler, this.cacheHandler, this.snapshotComparator, this.screenCapturer); this.screenCapturer = new ScreenCapturer_1.ScreenCapturer(this.snapshotManager, config.promptHandler); this.autoPerformer = new AutoPerformer_1.AutoPerformer(new AutoPerformerPromptCreator_1.AutoPerformerPromptCreator(), this.stepPerformer, config.promptHandler, this.screenCapturer, this.cacheHandler, this.snapshotComparator); } /** * Checks if Pilot is currently running a test flow. * @returns true if running, false otherwise */ assertIsRunning() { if (!this.running) { throw new PilotError_1.PilotError("Pilot is not running. Please call the `start()` method before performing a test step."); } } /** * Starts a new test flow session. * Must be called before any test operations to ensure a clean state, as Pilot uses operation history for context. */ start() { if (this.running) { throw new PilotError_1.PilotError("Pilot was already started. Please call the `end()` method before starting a new test flow."); } this.running = true; this.previousSteps = []; this.cacheHandler.clearTemporaryCache(); } /** * Ends the current test flow session and handles cache management. * @param shouldSaveInCache - If true, the current test flow will be saved in cache (default: true) */ end(shouldSaveInCache = true) { if (!this.running) { throw new PilotError_1.PilotError("Pilot is not running. Please call the `start()` method before ending the test flow."); } this.running = false; if (shouldSaveInCache) this.cacheHandler.flushTemporaryCache(); } /** * Extends the testing framework's API capabilities. * @param categories - Additional API categories to add * @param context - Testing framework variables to expose (optional) * @example * pilot.extendAPICatalog([ * { * title: 'Custom Actions', * items: [ * { * signature: 'customAction(param: string)', * description: 'Performs a custom action', * example: 'await customAction("param")', * guidelines: ['Use this action for specific test scenarios'] * } * ] * } * ], { customAction }); */ extendAPICatalog(categories, context) { this.stepPerformerPromptCreator.extendAPICategories(categories); if (context) this.stepPerformer.extendJSContext(context); } /** * Performs one or more test steps using the provided intents. * @param steps The intents describing the test steps to perform. * @returns The result of the last executed step. */ async perform(...steps) { this.loadCache(); let result; for await (const step of steps) { result = await this.performStep(step); } return result; } async performStep(step) { this.assertIsRunning(); const screenCapture = await this.screenCapturer.capture(true); const { code, result } = await this.stepPerformer.perform(step, this.previousSteps, screenCapture); this.didPerformStep(step, code, result); return result; } didPerformStep(step, code, result) { this.previousSteps = [ ...this.previousSteps, { step, code, result, }, ]; } /** * Performs an entire test flow using the provided goal. * @param goal A string which describes the flow should be executed. * @param reviewConfigs Optional review types to include in the autopilot report. * @returns pilot report with info about the actions thoughts etc ... */ async autopilot(goal, reviewConfigs) { this.loadCache(); this.assertIsRunning(); return await this.autoPerformer.perform(goal, reviewConfigs); } /** * Loads the cache from the cache file. */ loadCache() { this.cacheHandler.loadCacheFromFile(); } } exports.Pilot = Pilot;