@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
JavaScript
"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;