UNPKG

donobu

Version:

Create browser automations with an LLM agent and replay them as Playwright scripts.

176 lines 7.79 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.AssertPageTool = exports.AssertPageToolGptSchema = exports.AssertPageCoreSchema = void 0; const v4_1 = require("zod/v4"); const ToolCallResult_1 = require("../models/ToolCallResult"); const ToolSchema_1 = require("../models/ToolSchema"); const TargetUtils_1 = require("../utils/TargetUtils"); const Tool_1 = require("./Tool"); /** * Default timeout (in milliseconds) used by all page assertions. Matches Playwright's built‑in * `expect` helper default so behaviour is familiar. */ const DEFAULT_ASSERT_TIMEOUT = 5_000; exports.AssertPageCoreSchema = v4_1.z.object({ type: v4_1.z .enum(['url', 'title', 'content']) .describe('The type of assertion to perform on the current page'), expected: v4_1.z .string() .describe("The expected value to check for. For 'content' type, this is the text to find on the page. For 'title' type, this is the expected page title. For 'url' type, this is the expected URL. Can be a string for exact matching or a regex pattern when isRegex is true"), isRegex: v4_1.z .boolean() .optional() .default(false) .describe('Whether to treat the expected value as a regular expression pattern. Defaults to false'), }); exports.AssertPageToolGptSchema = v4_1.z.object({ ...ToolSchema_1.BaseGptArgsSchema.shape, ...exports.AssertPageCoreSchema.shape, }); class AssertPageTool extends Tool_1.Tool { constructor() { super(AssertPageTool.NAME, 'This method will test various aspects of the current page: URL, title, or visible content.', exports.AssertPageCoreSchema, exports.AssertPageToolGptSchema, false, undefined, ['web']); } async call(context, parameters) { const { type, expected, isRegex = false } = parameters; switch (type) { case 'content': return this.assertContent(context, expected, isRegex); case 'title': return this.assertTitle(context, expected, isRegex); case 'url': return this.assertUrl(context, expected, isRegex); default: return { isSuccessful: false, forLlm: `Invalid assertion type: "${type}". Valid types are: url, title, content.`, metadata: null, }; } } async assertContent(context, text, isRegex) { if (isRegex) { let regex; try { regex = new RegExp(text); } catch (err) { return { isSuccessful: false, forLlm: `Invalid regular expression pattern: "${text}". Error: ${err instanceof Error ? err.message : 'Unknown error'}`, metadata: null, }; } try { // Wait for content to appear with a timeout await (0, TargetUtils_1.webPage)(context).waitForFunction(({ regexSource }) => { const pageText = document.body.textContent || ''; return new RegExp(regexSource).test(pageText); }, { regexSource: regex.source }, { timeout: DEFAULT_ASSERT_TIMEOUT }); return ToolCallResult_1.ToolCallResult.successful(); } catch (_error) { return { isSuccessful: false, forLlm: `Failed to find text matching the pattern "${text}" anywhere in the page. Content assertion timed out after 5 seconds.`, metadata: null, }; } } try { // Wait for visible content to appear (up to 5s) await (0, TargetUtils_1.webPage)(context).waitForSelector(`text=${text}`, { state: 'visible', timeout: DEFAULT_ASSERT_TIMEOUT, }); return ToolCallResult_1.ToolCallResult.successful(); } catch (_error) { // Fallback to check if text exists but is not visible const locator = (0, TargetUtils_1.webPage)(context).getByText(text); const count = await locator.count(); return { isSuccessful: false, forLlm: count > 0 ? `The text "${text}" was found in the page, but its containing element was not visible. Content assertion timed out after 5 seconds.` : `Failed to find the text "${text}" anywhere in the page. Content assertion timed out after 5 seconds.`, metadata: null, }; } } async assertTitle(context, expectedTitle, isRegex) { let regex = null; if (isRegex) { try { regex = new RegExp(expectedTitle); } catch (err) { return { isSuccessful: false, forLlm: `Invalid regular expression pattern: "${expectedTitle}". Error: ${err instanceof Error ? err.message : 'Unknown error'}`, metadata: null, }; } } try { // Wait for SPA title updates with a timeout await (0, TargetUtils_1.webPage)(context).waitForFunction(({ expected, isRegex, regexSource }) => { const title = document.title; if (isRegex && regexSource) { return regexSource && new RegExp(regexSource).test(title); } return title === expected; }, { expected: expectedTitle, isRegex, regexSource: regex?.source }, { timeout: DEFAULT_ASSERT_TIMEOUT }); return ToolCallResult_1.ToolCallResult.successful(); } catch (_error) { const actualTitle = await (0, TargetUtils_1.webPage)(context).title(); return { isSuccessful: false, forLlm: isRegex ? `Expected page title to match the pattern "${expectedTitle}", but actual title is "${actualTitle}". Title assertion timed out after 5 seconds.` : `Expected page title to equal "${expectedTitle}", but actual title is "${actualTitle}". Title assertion timed out after 5 seconds.`, metadata: null, }; } } async assertUrl(context, expectedUrl, isRegex) { const actualUrl = (0, TargetUtils_1.webPage)(context).url(); if (!isRegex) { if (actualUrl === expectedUrl) { return ToolCallResult_1.ToolCallResult.successful(); } return { isSuccessful: false, forLlm: `Expected page URL to equal "${expectedUrl}", but actual URL is "${actualUrl}".`, metadata: null, }; } let regex; try { regex = new RegExp(expectedUrl); } catch (err) { return { isSuccessful: false, forLlm: `Invalid regular expression pattern: "${expectedUrl}". Error: ${err instanceof Error ? err.message : 'Unknown error'}`, metadata: null, }; } if (regex.test(actualUrl)) { return ToolCallResult_1.ToolCallResult.successful(); } return { isSuccessful: false, forLlm: `Expected page URL to match the pattern "${expectedUrl}", but actual URL is "${actualUrl}".`, metadata: null, }; } async callFromGpt(context, parameters) { return this.call(context, parameters); } } exports.AssertPageTool = AssertPageTool; AssertPageTool.NAME = 'assertPage'; //# sourceMappingURL=AssertPageTool.js.map