From 1ddefc14c17e08aa6e65467cd7daf1fe95899d16 Mon Sep 17 00:00:00 2001 From: Pavel Feldman Date: Wed, 5 Nov 2025 09:40:28 -0800 Subject: [PATCH] chore(agent): allow submitting structured plans --- .../agents/playwright-test-planner.agent.md | 49 +--------- .../playwright/src/mcp/test/plannerTools.ts | 90 +++++++++++++++++++ 2 files changed, 92 insertions(+), 47 deletions(-) diff --git a/packages/playwright/src/agents/playwright-test-planner.agent.md b/packages/playwright/src/agents/playwright-test-planner.agent.md index f74ccfdccd2a6..ce0780ba569ef 100644 --- a/packages/playwright/src/agents/playwright-test-planner.agent.md +++ b/packages/playwright/src/agents/playwright-test-planner.agent.md @@ -5,7 +5,6 @@ model: sonnet color: green tools: - search - - edit - playwright-test/browser_click - playwright-test/browser_close - playwright-test/browser_console_messages @@ -24,6 +23,7 @@ tools: - playwright-test/browser_type - playwright-test/browser_wait_for - playwright-test/planner_setup_page + - playwright-test/planner_save_plan --- You are an expert web test planner with extensive experience in quality assurance, user experience testing, and test @@ -61,52 +61,7 @@ You will: 5. **Create Documentation** - Save your test plan as requested: - - Executive summary of the tested page/application - - Individual scenarios as separate sections - - Each scenario formatted with numbered steps - - Each test case with proposed file name for implementation - - Clear expected results for verification - - -# TodoMVC Application - Comprehensive Test Plan - -## Application Overview - -The TodoMVC application is a React-based todo list manager that provides core task management functionality. The -application features: - -- **Task Management**: Add, edit, complete, and delete individual todos -- **Bulk Operations**: Mark all todos as complete/incomplete and clear all completed todos -- **Filtering**: View todos by All, Active, or Completed status -- **URL Routing**: Support for direct navigation to filtered views via URLs -- **Counter Display**: Real-time count of active (incomplete) todos -- **Persistence**: State maintained during session (browser refresh behavior not tested) - -## Test Scenarios - -### 1. Adding New Todos - -**Seed:** `tests/seed.spec.ts` - -#### 1.1 Add Valid Todo - -**File** `tests/adding-new-todos/add-valid-todo.spec.ts` - -**Steps:** -1. Click in the "What needs to be done?" input field -2. Type "Buy groceries" -3. Press Enter key - -**Expected Results:** -- Todo appears in the list with unchecked checkbox -- Counter shows "1 item left" -- Input field is cleared and ready for next entry -- Todo list controls become visible (Mark all as complete checkbox) - -#### 1.2 -... - + Submit your test plan using `planner_save_plan` tool. **Quality Standards**: - Write steps that are specific enough for any tester to follow diff --git a/packages/playwright/src/mcp/test/plannerTools.ts b/packages/playwright/src/mcp/test/plannerTools.ts index 02ef964151795..e405adc9aa82f 100644 --- a/packages/playwright/src/mcp/test/plannerTools.ts +++ b/packages/playwright/src/mcp/test/plannerTools.ts @@ -14,6 +14,9 @@ * limitations under the License. */ +import fs from 'fs'; +import path from 'path'; + import { z } from '../sdk/bundle'; import { defineTestTool } from './testTool'; @@ -35,3 +38,90 @@ export const setupPage = defineTestTool({ return { content: [] }; }, }); + +const planSchema = z.object({ + overview: z.string().describe('A brief overview of the application to be tested'), + suites: z.array(z.object({ + name: z.string().describe('The name of the suite'), + seedFile: z.string().describe('A seed file that was used to setup the page for testing.'), + tests: z.array(z.object({ + name: z.string().describe('The name of the test'), + file: z.string().describe('The file the test should be saved to, for example: "tests//.spec.ts".'), + steps: z.array(z.string().describe(`The steps to be executed to perform the test. For example: 'Click on the "Submit" button'`)), + expectedResults: z.array(z.string().describe('The expected results of the steps for test to verify.')), + })), + })), +}); + +export const submitTestPlan = defineTestTool({ + schema: { + name: 'planner_submit_plan', + title: 'Submit test plan', + description: 'Submit the test plan to the test planner', + inputSchema: planSchema, + type: 'readOnly', + }, + + handle: async (context, params) => { + return { + content: [{ + type: 'text', + text: JSON.stringify(params, null, 2), + }], + }; + }, +}); + +export const saveTestPlan = defineTestTool({ + schema: { + name: 'planner_save_plan', + title: 'Save test plan as markdown file', + description: 'Save the test plan as a markdown file', + inputSchema: planSchema.extend({ + name: z.string().describe('The name of the test plan, for example: "Test Plan".'), + fileName: z.string().describe('The file to save the test plan to, for example: "spec/test.plan.md". Relative to the workspace root.'), + }), + type: 'readOnly', + }, + + handle: async (context, params) => { + const lines: string[] = []; + lines.push(`# ${params.name}`); + lines.push(``); + lines.push(`## Application Overview`); + lines.push(``); + lines.push(params.overview); + lines.push(``); + lines.push(`## Test Scenarios`); + for (let i = 0; i < params.suites.length; i++) { + lines.push(``); + const suite = params.suites[i]; + lines.push(`### ${i + 1}. ${suite.name}`); + lines.push(``); + lines.push(`**Seed:** \`${suite.seedFile}\``); + for (let j = 0; j < suite.tests.length; j++) { + lines.push(``); + const test = suite.tests[j]; + lines.push(`#### ${i + 1}.${j + 1}. ${test.name}`); + lines.push(``); + lines.push(`**File:** \`${test.file}\``); + lines.push(``); + lines.push(`**Steps:**`); + for (let k = 0; k < test.steps.length; k++) + lines.push(` ${k + 1}. ${test.steps[k]}`); + lines.push(``); + lines.push(`**Expected Results:**`); + for (const result of test.expectedResults) + lines.push(` - ${result}`); + } + } + lines.push(``); + await fs.promises.writeFile(path.resolve(context.rootPath, params.fileName), lines.join('\n')); + return { + content: [{ + type: 'text', + text: `Test plan saved to ${params.fileName}`, + }], + }; + }, +});