|
| 1 | +/** |
| 2 | + * Copyright (c) Autify Inc. |
| 3 | + * |
| 4 | + * Licensed under the Apache License, Version 2.0 (the "License"); |
| 5 | + * you may not use this file except in compliance with the License. |
| 6 | + * You may obtain a copy of the License at |
| 7 | + * |
| 8 | + * http://www.apache.org/licenses/LICENSE-2.0 |
| 9 | + * |
| 10 | + * Unless required by applicable law or agreed to in writing, software |
| 11 | + * distributed under the License is distributed on an "AS IS" BASIS, |
| 12 | + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| 13 | + * See the License for the specific language governing permissions and |
| 14 | + * limitations under the License. |
| 15 | + */ |
| 16 | + |
| 17 | +import { z } from 'zod'; |
| 18 | +import { expect } from '@playwright/test'; |
| 19 | +import { replaceEnvVar } from './utils.js'; |
| 20 | + |
| 21 | +import { defineTool } from './tool.js'; |
| 22 | +import { generateLocator } from '../context.js'; |
| 23 | + |
| 24 | +const elementSchema = z.object({ |
| 25 | + element: z.string().describe('Human-readable element description used to obtain permission to interact with the element'), |
| 26 | + ref: z.string().describe('Exact target element reference from the page snapshot'), |
| 27 | +}); |
| 28 | + |
| 29 | +const assertContainTextSchema = elementSchema.partial().extend({ |
| 30 | + against: z.enum(['element', 'page']).describe('Assert against the specified element or the whole page. If page, element and ref are not needed.'), |
| 31 | + expected: z.string().describe('Expected text to be contained in the specified element or the whole page'), |
| 32 | +}); |
| 33 | + |
| 34 | +const assertContainText = defineTool({ |
| 35 | + capability: 'core', |
| 36 | + schema: { |
| 37 | + name: 'browser_assert_contain_text', |
| 38 | + description: 'Assert that the element or the whole page contains the expected text. It returns JSON having "result" (PASS or FAIL), "against" (assert against element or page) and "error" (details if result is FAIL).', |
| 39 | + inputSchema: assertContainTextSchema, |
| 40 | + }, |
| 41 | + |
| 42 | + handle: async (context, params) => { |
| 43 | + const validatedParams = assertContainTextSchema.parse(params); |
| 44 | + if (validatedParams.against === 'element') { |
| 45 | + if (validatedParams.ref === undefined) |
| 46 | + throw new Error('ref is required when asserting against an element'); |
| 47 | + const locator = context.currentTabOrDie().snapshotOrDie().refLocator(validatedParams.ref); |
| 48 | + const code = [ |
| 49 | + `// Assert ${params.element} contains ${params.expected}`, |
| 50 | + `await expect(page.${await generateLocator(locator)}).toContainText('${validatedParams.expected}');`, |
| 51 | + ]; |
| 52 | + return { |
| 53 | + code, |
| 54 | + action: async () => { |
| 55 | + try { |
| 56 | + await expect(locator).toContainText(replaceEnvVar(validatedParams.expected)); |
| 57 | + return { |
| 58 | + content: [{ |
| 59 | + type: 'text', |
| 60 | + text: JSON.stringify({ result: 'PASS', against: 'element' }), |
| 61 | + }], |
| 62 | + }; |
| 63 | + } catch (err) { |
| 64 | + const error = err instanceof Error ? err.message : String(err); |
| 65 | + return { |
| 66 | + content: [{ |
| 67 | + type: 'text', |
| 68 | + text: JSON.stringify({ result: 'FAIL', error, against: 'element' }), |
| 69 | + }], |
| 70 | + }; |
| 71 | + } |
| 72 | + }, |
| 73 | + captureSnapshot: false, |
| 74 | + waitForNetwork: false, |
| 75 | + }; |
| 76 | + } else { |
| 77 | + const locator = context.currentTabOrDie().page.locator('body'); |
| 78 | + const code = [ |
| 79 | + `// Assert page contains ${params.expected}`, |
| 80 | + `await expect(page.${await generateLocator(locator)}).toContainText('${validatedParams.expected}');`, |
| 81 | + ]; |
| 82 | + return { |
| 83 | + code, |
| 84 | + action: async () => { |
| 85 | + try { |
| 86 | + await expect(locator).toContainText(replaceEnvVar(validatedParams.expected)); |
| 87 | + return { |
| 88 | + content: [{ |
| 89 | + type: 'text', |
| 90 | + text: JSON.stringify({ result: 'PASS', against: 'page' }), |
| 91 | + }], |
| 92 | + }; |
| 93 | + } catch (err) { |
| 94 | + const error = err instanceof Error ? err.message : String(err); |
| 95 | + return { |
| 96 | + content: [{ |
| 97 | + type: 'text', |
| 98 | + text: JSON.stringify({ result: 'FAIL', error, against: 'page' }), |
| 99 | + }], |
| 100 | + }; |
| 101 | + } |
| 102 | + }, |
| 103 | + captureSnapshot: false, |
| 104 | + waitForNetwork: false, |
| 105 | + }; |
| 106 | + } |
| 107 | + }, |
| 108 | +}); |
| 109 | + |
| 110 | +export default [assertContainText]; |
0 commit comments