From d2c267c1d214bf9fd56a0a5ad2c9a34a8827634b Mon Sep 17 00:00:00 2001 From: Pavel Feldman Date: Thu, 2 Oct 2025 12:02:26 -0700 Subject: [PATCH 01/19] cherry-pick(#37689): Revert "fix(trace): should survive ping as the first command after restart" --- packages/trace-viewer/src/sw/main.ts | 43 ++++++++++++++-------------- tests/library/trace-viewer.spec.ts | 20 ------------- 2 files changed, 21 insertions(+), 42 deletions(-) diff --git a/packages/trace-viewer/src/sw/main.ts b/packages/trace-viewer/src/sw/main.ts index c07323d05d244..0e7ff887cc332 100644 --- a/packages/trace-viewer/src/sw/main.ts +++ b/packages/trace-viewer/src/sw/main.ts @@ -93,19 +93,6 @@ async function doFetch(event: FetchEvent): Promise { const request = event.request; const client = await self.clients.get(event.clientId); - const urlInScope = request.url.startsWith(self.registration.scope) ? new URL(unwrapPopoutUrl(request.url)) : undefined; - const relativePath = urlInScope?.pathname.substring(scopePath.length - 1); - - if (relativePath !== '/contexts' && !clientIdToTraceUrls.has(event.clientId)) { - // Service worker was restarted upon subresource fetch. - // It was stopped because ping did not keep it alive since the tab itself was throttled. - const params = await loadClientIdParams(event.clientId); - if (params) { - for (const traceUrl of params.traceUrls) - await loadTrace(traceUrl, null, client, params.limit, () => {}); - } - } - // When trace viewer is deployed over https, we will force upgrade // insecure http subresources to https. Otherwise, these will fail // to load inside our https snapshots. @@ -113,7 +100,9 @@ async function doFetch(event: FetchEvent): Promise { // the https urls. const isDeployedAsHttps = self.registration.scope.startsWith('https://'); - if (urlInScope && relativePath) { + if (request.url.startsWith(self.registration.scope)) { + const url = new URL(unwrapPopoutUrl(request.url)); + const relativePath = url.pathname.substring(scopePath.length - 1); if (relativePath === '/ping') { await gc(); return new Response(null, { status: 200 }); @@ -123,12 +112,12 @@ async function doFetch(event: FetchEvent): Promise { return new Response(null, { status: 200 }); } - const traceUrl = urlInScope.searchParams.get('trace'); + const traceUrl = url.searchParams.get('trace'); if (relativePath === '/contexts') { try { - const limit = urlInScope.searchParams.has('limit') ? +urlInScope.searchParams.get('limit')! : undefined; - const traceModel = await loadTrace(traceUrl!, urlInScope.searchParams.get('traceFileName'), client, limit, (done: number, total: number) => { + const limit = url.searchParams.has('limit') ? +url.searchParams.get('limit')! : undefined; + const traceModel = await loadTrace(traceUrl!, url.searchParams.get('traceFileName'), client, limit, (done: number, total: number) => { client.postMessage({ method: 'progress', params: { done, total } }); }); return new Response(JSON.stringify(traceModel!.contextEntries), { @@ -143,12 +132,22 @@ async function doFetch(event: FetchEvent): Promise { } } + if (!clientIdToTraceUrls.has(event.clientId)) { + // Service worker was restarted upon subresource fetch. + // It was stopped because ping did not keep it alive since the tab itself was throttled. + const params = await loadClientIdParams(event.clientId); + if (params) { + for (const traceUrl of params.traceUrls) + await loadTrace(traceUrl, null, client, params.limit, () => {}); + } + } + if (relativePath.startsWith('/snapshotInfo/')) { const { snapshotServer } = loadedTraces.get(traceUrl!) || {}; if (!snapshotServer) return new Response(null, { status: 404 }); const pageOrFrameId = relativePath.substring('/snapshotInfo/'.length); - return snapshotServer.serveSnapshotInfo(pageOrFrameId, urlInScope.searchParams); + return snapshotServer.serveSnapshotInfo(pageOrFrameId, url.searchParams); } if (relativePath.startsWith('/snapshot/')) { @@ -156,7 +155,7 @@ async function doFetch(event: FetchEvent): Promise { if (!snapshotServer) return new Response(null, { status: 404 }); const pageOrFrameId = relativePath.substring('/snapshot/'.length); - const response = snapshotServer.serveSnapshot(pageOrFrameId, urlInScope.searchParams, urlInScope.href); + const response = snapshotServer.serveSnapshot(pageOrFrameId, url.searchParams, url.href); if (isDeployedAsHttps) response.headers.set('Content-Security-Policy', 'upgrade-insecure-requests'); return response; @@ -167,7 +166,7 @@ async function doFetch(event: FetchEvent): Promise { if (!snapshotServer) return new Response(null, { status: 404 }); const pageOrFrameId = relativePath.substring('/closest-screenshot/'.length); - return snapshotServer.serveClosestScreenshot(pageOrFrameId, urlInScope.searchParams); + return snapshotServer.serveClosestScreenshot(pageOrFrameId, url.searchParams); } if (relativePath.startsWith('/sha1/')) { @@ -176,13 +175,13 @@ async function doFetch(event: FetchEvent): Promise { for (const trace of loadedTraces.values()) { const blob = await trace.traceModel.resourceForSha1(sha1); if (blob) - return new Response(blob, { status: 200, headers: downloadHeaders(urlInScope.searchParams) }); + return new Response(blob, { status: 200, headers: downloadHeaders(url.searchParams) }); } return new Response(null, { status: 404 }); } if (relativePath.startsWith('/file/')) { - const path = urlInScope.searchParams.get('path')!; + const path = url.searchParams.get('path')!; const traceViewerServer = clientIdToTraceUrls.get(event.clientId ?? '')?.traceViewerServer; if (!traceViewerServer) throw new Error('client is not initialized'); diff --git a/tests/library/trace-viewer.spec.ts b/tests/library/trace-viewer.spec.ts index 7a9a45e9c18ab..eea630c49decc 100644 --- a/tests/library/trace-viewer.spec.ts +++ b/tests/library/trace-viewer.spec.ts @@ -2056,23 +2056,3 @@ test('should survive service worker restart', async ({ page, runAndTrace, server const snapshot2 = await traceViewer.snapshotFrame('Set content'); await expect(snapshot2.locator('body')).toHaveText('Old world'); }); - -test('should survive ping after service worker restart', async ({ page, runAndTrace, server }) => { - const traceViewer = await runAndTrace(async () => { - await page.goto(server.EMPTY_PAGE); - await page.setContent('Old world'); - await page.evaluate(() => document.body.textContent = 'New world'); - }); - const snapshot1 = await traceViewer.snapshotFrame('Evaluate'); - await expect(snapshot1.locator('body')).toHaveText('New world'); - - const status = await traceViewer.page.evaluate(async () => { - const response1 = await fetch('restartServiceWorker'); - const response2 = await fetch('ping'); - return response1.status + '/' + response2.status; - }); - expect(status).toBe('200/200'); - - const snapshot2 = await traceViewer.snapshotFrame('Set content'); - await expect(snapshot2.locator('body')).toHaveText('Old world'); -}); From 6d13fbe6bfc87e5cb4f5ce0a33f7dd6171ff94c0 Mon Sep 17 00:00:00 2001 From: Pavel Feldman Date: Thu, 2 Oct 2025 12:11:32 -0700 Subject: [PATCH 02/19] cherry-pick(#37690): Revert "fix(trace): survive sw restart" --- package-lock.json | 7 ---- packages/trace-viewer/package.json | 1 - packages/trace-viewer/src/sw/main.ts | 54 ++-------------------------- tests/library/trace-viewer.spec.ts | 19 ---------- 4 files changed, 2 insertions(+), 79 deletions(-) diff --git a/package-lock.json b/package-lock.json index 0396d7fc631de..03159dae8b097 100644 --- a/package-lock.json +++ b/package-lock.json @@ -4797,12 +4797,6 @@ "node": ">=0.10.0" } }, - "node_modules/idb-keyval": { - "version": "6.2.2", - "resolved": "https://registry.npmjs.org/idb-keyval/-/idb-keyval-6.2.2.tgz", - "integrity": "sha512-yjD9nARJ/jb1g+CvD0tlhUHOrJ9Sy0P8T9MF3YaLlHnSRpwPfpTX0XIvpmw3gAJUmEu3FiICLBDPXVwyEvrleg==", - "license": "Apache-2.0" - }, "node_modules/ignore": { "version": "5.3.2", "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", @@ -8374,7 +8368,6 @@ "packages/trace-viewer": { "version": "0.0.0", "dependencies": { - "idb-keyval": "^6.2.2", "yaml": "^2.6.0" } }, diff --git a/packages/trace-viewer/package.json b/packages/trace-viewer/package.json index 699014a40fc7a..57a3dfd4094f3 100644 --- a/packages/trace-viewer/package.json +++ b/packages/trace-viewer/package.json @@ -4,7 +4,6 @@ "version": "0.0.0", "type": "module", "dependencies": { - "idb-keyval": "^6.2.2", "yaml": "^2.6.0" } } diff --git a/packages/trace-viewer/src/sw/main.ts b/packages/trace-viewer/src/sw/main.ts index 0e7ff887cc332..cdce3261feac0 100644 --- a/packages/trace-viewer/src/sw/main.ts +++ b/packages/trace-viewer/src/sw/main.ts @@ -14,8 +14,6 @@ * limitations under the License. */ -import * as idbKeyval from 'idb-keyval'; - import { splitProgress } from './progress'; import { unwrapPopoutUrl } from './snapshotRenderer'; import { SnapshotServer } from './snapshotServer'; @@ -35,13 +33,10 @@ self.addEventListener('activate', function(event: any) { }); const scopePath = new URL(self.registration.scope).pathname; + const loadedTraces = new Map(); -const clientIdToTraceUrls = new Map, traceViewerServer: TraceViewerServer }>(); -function simulateServiceWorkerRestart() { - loadedTraces.clear(); - clientIdToTraceUrls.clear(); -} +const clientIdToTraceUrls = new Map, traceViewerServer: TraceViewerServer }>(); async function loadTrace(traceUrl: string, traceFileName: string | null, client: any | undefined, limit: number | undefined, progress: (done: number, total: number) => undefined): Promise { await gc(); @@ -54,7 +49,6 @@ async function loadTrace(traceUrl: string, traceFileName: string | null, client: clientIdToTraceUrls.set(clientId, data); } data.traceUrls.add(traceUrl); - await saveClientIdParams(); const traceModel = new TraceModel(); try { @@ -107,10 +101,6 @@ async function doFetch(event: FetchEvent): Promise { await gc(); return new Response(null, { status: 200 }); } - if (relativePath === '/restartServiceWorker') { - simulateServiceWorkerRestart(); - return new Response(null, { status: 200 }); - } const traceUrl = url.searchParams.get('trace'); @@ -132,16 +122,6 @@ async function doFetch(event: FetchEvent): Promise { } } - if (!clientIdToTraceUrls.has(event.clientId)) { - // Service worker was restarted upon subresource fetch. - // It was stopped because ping did not keep it alive since the tab itself was throttled. - const params = await loadClientIdParams(event.clientId); - if (params) { - for (const traceUrl of params.traceUrls) - await loadTrace(traceUrl, null, client, params.limit, () => {}); - } - } - if (relativePath.startsWith('/snapshotInfo/')) { const { snapshotServer } = loadedTraces.get(traceUrl!) || {}; if (!snapshotServer) @@ -241,36 +221,6 @@ async function gc() { if (!usedTraces.has(traceUrl)) loadedTraces.delete(traceUrl); } - - await saveClientIdParams(); -} - -// Persist clientIdToTraceUrls to localStorage to avoid losing it when the service worker is restarted. -async function saveClientIdParams() { - const serialized: Record = {}; - for (const [clientId, data] of clientIdToTraceUrls) { - serialized[clientId] = { - limit: data.limit, - traceUrls: [...data.traceUrls] - }; - } - - const newValue = JSON.stringify(serialized); - const oldValue = await idbKeyval.get('clientIdToTraceUrls'); - if (newValue === oldValue) - return; - idbKeyval.set('clientIdToTraceUrls', newValue); -} - -async function loadClientIdParams(clientId: string): Promise<{ limit: number | undefined, traceUrls: string[] } | undefined> { - const serialized = await idbKeyval.get('clientIdToTraceUrls') as string | undefined; - if (!serialized) - return; - const deserialized = JSON.parse(serialized); - return deserialized[clientId]; } // @ts-ignore diff --git a/tests/library/trace-viewer.spec.ts b/tests/library/trace-viewer.spec.ts index eea630c49decc..e888559a0f72b 100644 --- a/tests/library/trace-viewer.spec.ts +++ b/tests/library/trace-viewer.spec.ts @@ -2037,22 +2037,3 @@ test.describe(() => { await expect(frame.getByRole('button')).toHaveCSS('color', 'rgb(255, 0, 0)'); }); }); - -test('should survive service worker restart', async ({ page, runAndTrace, server }) => { - const traceViewer = await runAndTrace(async () => { - await page.goto(server.EMPTY_PAGE); - await page.setContent('Old world'); - await page.evaluate(() => document.body.textContent = 'New world'); - }); - const snapshot1 = await traceViewer.snapshotFrame('Evaluate'); - await expect(snapshot1.locator('body')).toHaveText('New world'); - - const status = await traceViewer.page.evaluate(async () => { - const response = await fetch('restartServiceWorker'); - return response.status; - }); - expect(status).toBe(200); - - const snapshot2 = await traceViewer.snapshotFrame('Set content'); - await expect(snapshot2.locator('body')).toHaveText('Old world'); -}); From a5c44375810988c4db21ef136f73106116e99a55 Mon Sep 17 00:00:00 2001 From: Pavel Feldman Date: Thu, 2 Oct 2025 15:48:49 -0700 Subject: [PATCH 03/19] cherry-pick(#37693): docs: use VS Code images for test agents --- .../images/test-agents/generator-prompt.png | Bin 0 -> 34718 bytes docs/src/images/test-agents/healer-prompt.png | Bin 0 -> 28393 bytes .../src/images/test-agents/planner-prompt.png | Bin 0 -> 32863 bytes docs/src/test-agents-js.md | 240 +++++++++--------- examples/todomvc/tests/seed.spec.ts | 2 - 5 files changed, 118 insertions(+), 124 deletions(-) create mode 100644 docs/src/images/test-agents/generator-prompt.png create mode 100644 docs/src/images/test-agents/healer-prompt.png create mode 100644 docs/src/images/test-agents/planner-prompt.png diff --git a/docs/src/images/test-agents/generator-prompt.png b/docs/src/images/test-agents/generator-prompt.png new file mode 100644 index 0000000000000000000000000000000000000000..56c67cf3ce8b2e027acd979f812980b9629925b0 GIT binary patch literal 34718 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 DEo27{ literal 0 HcmV?d00001 diff --git a/docs/src/images/test-agents/healer-prompt.png b/docs/src/images/test-agents/healer-prompt.png new file mode 100644 index 0000000000000000000000000000000000000000..5b6116972cfb69d06226a14385b9c9db685f8110 GIT binary patch literal 28393 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 ZlXEJpo-%tM0#~jnE2znrJ~V#)KLAt<*;oJo literal 0 HcmV?d00001 diff --git a/docs/src/test-agents-js.md b/docs/src/test-agents-js.md index 3b7c7d46705e0..b608ab7909cdb 100644 --- a/docs/src/test-agents-js.md +++ b/docs/src/test-agents-js.md @@ -5,108 +5,114 @@ title: "Agents" # Playwright Agents -## Test Coverage in 1-2-3 +Playwright comes with three Playwright Agents out of the box: **🎭 planner**, **🎭 generator** and **🎭 healer**. -Playwright’s agentic workflow makes it possible to generate test coverage in three straightforward steps. -These steps can be performed independently, manually, or as chained calls in an agentic loop. +These agents can be used independently, sequentially, or as the chained calls in the agentic loop. +Using them sequentially will produce test coverage for your product. -1. **Plan**: A planning agent explores the app and produces a test plan in `specs/*.md`. +* **🎭 planner** explores the app and produces a Markdown test plan -2. **Generate**: A generating agent transforms the plan into `tests/*.spec.ts` files. It executes actions against your site to verify selectors and flows, then emits testing code and assertions. +* **🎭 generator** transforms the Markdown plan into the Playwright Test files -3. **Heal**: A healing agent executes the test suite and automatically repairs failing tests by applying diffs in place. +* **🎭 healer** executes the test suite and automatically repairs failing tests ### Getting Started -In order to use Playwright Agents, you must add their definitions to your project using +Start with adding Playwright Agent definitions to your project using the `init-agents` command. These definitions should be regenerated whenever Playwright -is updated. +is updated to pick up new tools and instructions. -You need to run this command for each agentic loop you will be using: - -```bash -# Generate agent files for each agentic loop -# Visual Studio Code +```bash tab=bash-vscode npx playwright init-agents --loop=vscode -# Claude Code +``` + +```bash tab=bash-claude npx playwright init-agents --loop=claude -# opencode +``` + +```bash tab=bash-opencode npx playwright init-agents --loop=opencode ``` -Once the agents have been generated, you can use your AI tool of choice to command these agents to build Playwright Tests. Playwright splits this into three steps with one agent per step: +Once the agents have been generated, you can use your AI tool of choice to command these agents to build Playwright Tests. + -## 1. Plan +## 🎭 Planner -The planning agent explores your app environment and produces a test plan for one or many scenarios and user flows. +Planner agent explores your app and produces a test plan for one or many scenarios and user flows. **Input** -* A clear request to the planning agent (e.g., “Generate a plan for guest checkout.”) -* A live app entry point (URL) or a seed Playwright test that sets up the environment necessary to talk to your app -* A Product Requirement Document (PRD) (optional) +* A clear request to the planner (e.g., “Generate a plan for guest checkout.”) +* A `seed test` that sets up the environment necessary to interact with your app +* *(optional)* A Product Requirement Document (PRD) for context -**Example Prompt** +**Prompt** + +planner prompt -```markdown - Generate a test plan for "Guest Checkout" scenario. - Use `seed.spec.ts` as a seed test for the plan. +> - Notice how the `seed.spec.ts` is included in the context of the planner. +> - Planner will run this test to execute all the initialization necessary for your test including the global setup, project dependencies and all the necessary fixtures and hooks. +> - Planner will also use this seed test as an example of all the generated tests. Alternatively, you can mention the file name in the prompt. + +```js title="Example: seed.spec.ts" +import { test, expect } from './fixtures'; + +test('seed', async ({ page }) => { + // this test uses custom fixtures from ./fixtures +}); ``` **Output** -* A Markdown test plan saved to `specs/[scenario name].md`. The plan is human-readable but precise enough for test generation. +* A Markdown test plan saved as `specs/basic-operations.md`. +* The plan is human-readable but precise enough for test generation.
-Example: specs/guest-checkout.md +Example: specs/basic-operations.md ```markdown -# Feature: Guest Checkout +# TodoMVC Application - Basic Operations Test Plan -## Purpose -Allow a user to purchase without creating an account. +## Application Overview -## Preconditions -- Test seed `tests/seed.spec.ts`. -- Payment sandbox credentials available via env vars. +The TodoMVC application is a React-based todo list manager that demonstrates standard todo application functionality. The application provides comprehensive task management capabilities with a clean, intuitive interface. Key features include: -## Scenarios +- **Task Management**: Add, edit, complete, and delete individual todos +- **Bulk Operations**: Mark all todos as complete/incomplete and clear all completed todos +- **Filtering System**: View todos by All, Active, or Completed status with URL routing support +- **Real-time Counter**: Display of active (incomplete) todo count +- **Interactive UI**: Hover states, edit-in-place functionality, and responsive design +- **State Persistence**: Maintains state during session navigation -### SC-1: Add single item to cart and purchase -**Steps** -1. Open home page. -2. Search for "Wireless Mouse". -3. Open product page and add to cart. -4. Proceed to checkout as guest. -5. Fill shipping and payment details. -6. Confirm order. +## Test Scenarios -**Expected** -- Cart count increments after item is added. -- Checkout page shows item, price, tax, and total. -- Order confirmation number appears; status is "Processing". +### 1. Adding New Todos -### SC-2: Tax and shipping recalculation on address change -**Steps** -1. Start checkout with a CA address. -2. Change state to NY. +**Seed:** `tests/seed.spec.ts` -**Expected** -- Tax and shipping values recalculate. +#### 1.1 Add Valid Todo -## Data -- Product SKU: `WM-123` -- Payment: sandbox card `4111 1111 1111 1111`, valid expiry, CVV `123`. +**Steps:** +1. Click in the "What needs to be done?" input field +2. Type "Buy groceries" +3. Press Enter key -## Methodology -*Optional notes about testing methodology* +**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 Add Multiple Todos +... ```
-## 2. Generate +## 🎭 Generator -The generating agent uses the Markdown plan to produce executable Playwright tests. -It verifies selectors and assertions live against the application. Playwright supports +Generator agent uses the Markdown plan to produce executable Playwright Tests. +It verifies selectors and assertions live as it performs the scenarios. Playwright supports generation hints and provides a catalog of assertions for efficient structural and behavioral validation. @@ -114,11 +120,12 @@ behavioral validation. * Markdown plan from `specs/` -**Example Prompt** +**Prompt** -```markdown - Generate tests for the guest checkout plan under `specs/`. -``` +generator prompt + +> - Notice how the `basic-operations.md` is included in the context of the generator. +> - This is how generator knows where to get the test plan from. Alternatively, you can mention the file name in the prompt. **Output** @@ -126,57 +133,50 @@ behavioral validation. * Generated tests may include initial errors that can be healed automatically by the healer agent
-Example: tests/guest-checkout.spec.ts +Example: tests/add-valid-todo.spec.ts ```ts -import { test, expect } from '@playwright/test'; - -test.describe('Guest Checkout', () => { - test('SC-1: add item and purchase', async ({ page }) => { - await page.goto('/'); - await page.getByRole('searchbox', { name: /search/i }).fill('Wireless Mouse'); - await page.getByRole('button', { name: /search/i }).click(); - - await page.getByRole('link', { name: /wireless mouse/i }).click(); - await page.getByRole('button', { name: /add to cart/i }).click(); - - // Assertion: cart badge increments - await expect(page.getByTestId('cart-badge')).toHaveText('1'); - - await page.getByRole('link', { name: /checkout/i }).click(); - await page.getByRole('button', { name: /continue as guest/i }).click(); - - // Fill checkout form - await page.getByLabel('Email').fill(process.env.CHECKOUT_EMAIL!); - await page.getByLabel('Full name').fill('Alex Guest'); - await page.getByLabel('Address').fill('1 Market St'); - await page.getByLabel('City').fill('San Francisco'); - await page.getByLabel('State').selectOption('CA'); - await page.getByLabel('ZIP').fill('94105'); - - // Payment (sandbox) - const frame = page.frameLocator('[data-testid="card-iframe"]'); - await frame.getByLabel('Card number').fill('4111111111111111'); - await frame.getByLabel('MM / YY').fill('12/30'); - await frame.getByLabel('CVC').fill('123'); - - await page.getByRole('button', { name: /pay/i }).click(); - - // Assertions: confirmation invariants - await expect(page).toHaveURL(/\/orders\/\w+\/confirmation/); - await expect(page.getByRole('heading', { name: /thank you/i })).toBeVisible(); - await expect(page.getByTestId('order-status')).toHaveText(/processing/i); - - // Optional visual check - await expect(page.locator('[data-testid="order-summary"]')).toHaveScreenshot(); +// spec: specs/basic-operations.md +// seed: tests/seed.spec.ts + +import { test, expect } from '../fixtures'; + +test.describe('Adding New Todos', () => { + test('Add Valid Todo', async ({ page }) => { + // 1. Click in the "What needs to be done?" input field + const todoInput = page.getByRole('textbox', { name: 'What needs to be done?' }); + await todoInput.click(); + + // 2. Type "Buy groceries" + await todoInput.fill('Buy groceries'); + + // 3. Press Enter key + await todoInput.press('Enter'); + + // Expected Results: + // - Todo appears in the list with unchecked checkbox + await expect(page.getByText('Buy groceries')).toBeVisible(); + const todoCheckbox = page.getByRole('checkbox', { name: 'Toggle Todo' }); + await expect(todoCheckbox).toBeVisible(); + await expect(todoCheckbox).not.toBeChecked(); + + // - Counter shows "1 item left" + await expect(page.getByText('1 item left')).toBeVisible(); + + // - Input field is cleared and ready for next entry + await expect(todoInput).toHaveValue(''); + await expect(todoInput).toBeFocused(); + + // - Todo list controls become visible (Mark all as complete checkbox) + await expect(page.getByRole('checkbox', { name: '❯Mark all as complete' })).toBeVisible(); }); }); ```
-## 3. Heal +## 🎭 Healer -When a test fails, the healing agent: +When the test fails, the healer agent: * Replays the failing steps * Inspects the current UI to locate equivalent elements or flows @@ -187,15 +187,13 @@ When a test fails, the healing agent: * Failing test name -**Example Prompt** +**Prompt** -```markdown - Fix all failing tests for the guest checkout scenario. -``` +healer prompt **Output** -* A passing test, or a skipped test if the healer was unable to ensure correct functionality +* A passing test, or a skipped test if the healer believes the that functionality is broken. ## Artifacts and Conventions @@ -203,26 +201,24 @@ The static agent definitions and generated files follow a simple, auditable stru ```bash repo/ - .{claude|copilot|vscode|...}/ # agent definitions, tools, guardrails - specs/ # human-readable test plans - checkout-guest.md - account-settings.md - tests/ # generated Playwright tests - seed.spec.ts - checkout-guest.spec.ts - account-settings.spec.ts + .github/ # agent definitions + specs/ # human-readable test plans + basic-operations.md + tests/ # generated Playwright tests + seed.spec.ts # seed test for environment + tests/create/add-valid-todo.spec.ts playwright.config.ts ``` ### Agent Definitions -Agent definitions are collections of instructions and MCP tools. They are provided by +Under the hood, agent definitions are collections of instructions and MCP tools. They are provided by Playwright and should be regenerated whenever Playwright is updated. Example for Claude Code subagents: ```bash -npx playwright init-agents --loop=claude +npx playwright init-agents --loop=vscode ``` ### Specs in `specs/` diff --git a/examples/todomvc/tests/seed.spec.ts b/examples/todomvc/tests/seed.spec.ts index 43fd45c83a8a7..24cf199121ca9 100644 --- a/examples/todomvc/tests/seed.spec.ts +++ b/examples/todomvc/tests/seed.spec.ts @@ -1,5 +1,3 @@ -/* eslint-disable notice/notice */ - import { test, expect } from './fixtures'; test('seed', async ({ page }) => { From 0a91ecfa60c250e8ea4371894af717ee5d62e07d Mon Sep 17 00:00:00 2001 From: Pavel Feldman Date: Thu, 2 Oct 2025 17:32:30 -0700 Subject: [PATCH 04/19] cherry-pick(#37694): chore: move best practices into the journal --- .../.claude/agents/playwright-test-generator.md | 15 ++------------- .../\360\237\216\255 generator.chatmode.md" | 13 +------------ packages/playwright/src/agents/generator.md | 13 +------------ packages/playwright/src/mcp/test/testContext.ts | 14 ++++++++++++++ tests/mcp/generator.spec.ts | 16 ++++++++++++---- 5 files changed, 30 insertions(+), 41 deletions(-) diff --git a/examples/todomvc/.claude/agents/playwright-test-generator.md b/examples/todomvc/.claude/agents/playwright-test-generator.md index 0296e3e5e8534..b82e9bc3a13cd 100644 --- a/examples/todomvc/.claude/agents/playwright-test-generator.md +++ b/examples/todomvc/.claude/agents/playwright-test-generator.md @@ -24,6 +24,7 @@ application behavior. - Test title must match the scenario name - Includes a comment with the step text before each step execution. Do not duplicate comments if step requires multiple actions. + - Always use best practices from the log when generating tests. For following plan: @@ -55,16 +56,4 @@ application behavior. }); }); ``` - - -# Best practices -- Each test has clear, descriptive assertions that validate the expected behavior -- Includes proper error handling and meaningful failure messages -- Uses Playwright best practices (page.waitForLoadState, expect.toBeVisible, etc.) -- Do not improvise, do not add directives that were not asked for -- Uses reliable locators (preferring data-testid, role-based, or text-based selectors over fragile CSS selectors) -- Uses local variables for locators that are used multiple times -- Uses explicit waits rather than arbitrary timeouts -- Never waits for networkidle or use other discouraged or deprecated apis -- Is self-contained and can run independently -- Is deterministic and not prone to flaky behavior \ No newline at end of file + \ No newline at end of file diff --git "a/examples/todomvc/.github/chatmodes/\360\237\216\255 generator.chatmode.md" "b/examples/todomvc/.github/chatmodes/\360\237\216\255 generator.chatmode.md" index 88947303d7dc2..6ada1226f41ea 100644 --- "a/examples/todomvc/.github/chatmodes/\360\237\216\255 generator.chatmode.md" +++ "b/examples/todomvc/.github/chatmodes/\360\237\216\255 generator.chatmode.md" @@ -21,6 +21,7 @@ application behavior. - Test title must match the scenario name - Includes a comment with the step text before each step execution. Do not duplicate comments if step requires multiple actions. + - Always use best practices from the log when generating tests. For following plan: @@ -53,17 +54,5 @@ application behavior. }); ``` - -# Best practices -- Each test has clear, descriptive assertions that validate the expected behavior -- Includes proper error handling and meaningful failure messages -- Uses Playwright best practices (page.waitForLoadState, expect.toBeVisible, etc.) -- Do not improvise, do not add directives that were not asked for -- Uses reliable locators (preferring data-testid, role-based, or text-based selectors over fragile CSS selectors) -- Uses local variables for locators that are used multiple times -- Uses explicit waits rather than arbitrary timeouts -- Never waits for networkidle or use other discouraged or deprecated apis -- Is self-contained and can run independently -- Is deterministic and not prone to flaky behavior Context: User wants to test a login flow on their web application. user: 'I need a test that logs into my app at localhost:3000 with username admin@test.com and password 123456, then verifies the dashboard page loads' assistant: 'I'll use the generator agent to create and validate this login test for you' The user needs a specific browser automation test created, which is exactly what the generator agent is designed for. Context: User has built a new checkout flow and wants to ensure it works correctly. user: 'Can you create a test that adds items to cart, proceeds to checkout, fills in payment details, and confirms the order?' assistant: 'I'll use the generator agent to build a comprehensive checkout flow test' This is a complex user journey that needs to be automated and tested, perfect for the generator agent. \ No newline at end of file diff --git a/packages/playwright/src/agents/generator.md b/packages/playwright/src/agents/generator.md index dabbb46f14b1d..3e83d145e7e98 100644 --- a/packages/playwright/src/agents/generator.md +++ b/packages/playwright/src/agents/generator.md @@ -46,6 +46,7 @@ application behavior. - Test title must match the scenario name - Includes a comment with the step text before each step execution. Do not duplicate comments if step requires multiple actions. + - Always use best practices from the log when generating tests. For following plan: @@ -79,18 +80,6 @@ application behavior. ``` -# Best practices -- Each test has clear, descriptive assertions that validate the expected behavior -- Includes proper error handling and meaningful failure messages -- Uses Playwright best practices (page.waitForLoadState, expect.toBeVisible, etc.) -- Do not improvise, do not add directives that were not asked for -- Uses reliable locators (preferring data-testid, role-based, or text-based selectors over fragile CSS selectors) -- Uses local variables for locators that are used multiple times -- Uses explicit waits rather than arbitrary timeouts -- Never waits for networkidle or use other discouraged or deprecated apis -- Is self-contained and can run independently -- Is deterministic and not prone to flaky behavior - Context: User wants to test a login flow on their web application. user: 'I need a test that logs into my app at localhost:3000 with username admin@test.com and password 123456, then diff --git a/packages/playwright/src/mcp/test/testContext.ts b/packages/playwright/src/mcp/test/testContext.ts index 6148425616ab7..a2204e24db08b 100644 --- a/packages/playwright/src/mcp/test/testContext.ts +++ b/packages/playwright/src/mcp/test/testContext.ts @@ -65,6 +65,7 @@ export class GeneratorJournal { \`\`\`ts ${step.code} \`\`\``).join('\n\n')); + result.push(bestPracticesMarkdown); return result.join('\n\n'); } } @@ -177,3 +178,16 @@ export class TestContext { async close() { } } + +const bestPracticesMarkdown = ` +# Best practices +- Do not improvise, do not add directives that were not asked for +- Use clear, descriptive assertions to validate the expected behavior +- Use reliable locators from this log +- Use local variables for locators that are used multiple times +- Use Playwright waiting assertions and best practices from this log +- NEVER! use page.waitForLoadState() +- NEVER! use page.waitForNavigation() +- NEVER! use page.waitForTimeout() +- NEVER! use page.evaluate() +`; diff --git a/tests/mcp/generator.spec.ts b/tests/mcp/generator.spec.ts index 1ec5ca47ee519..8f7ffec24eb14 100644 --- a/tests/mcp/generator.spec.ts +++ b/tests/mcp/generator.spec.ts @@ -101,7 +101,7 @@ test('generator_setup_page', async ({ startClient }) => { expect(await client.callTool({ name: 'generator_read_log', arguments: {}, - })).toHaveTextResponse(`# Plan + })).toHaveTextResponse(expect.stringContaining(`# Plan Test plan @@ -125,7 +125,11 @@ Test plan ### Click submit button \`\`\`ts await page.getByRole('button', { name: 'Submit' }).click(); -\`\`\``); +\`\`\` + + +# Best practices +`)); }); test('click after generator_log_action', async ({ startClient }) => { @@ -164,7 +168,7 @@ test('click after generator_log_action', async ({ startClient }) => { expect(await client.callTool({ name: 'generator_read_log', arguments: {}, - })).toHaveTextResponse(`# Plan + })).toHaveTextResponse(expect.stringContaining(`# Plan Test plan @@ -188,7 +192,11 @@ Test plan ### Click submit button \`\`\`ts await page.getByRole('button', { name: 'Submit' }).click(); -\`\`\``); +\`\`\` + + +# Best practices +`)); }); test('generator_setup_page is required', async ({ startClient }) => { From 2c5e33dd4494601c29b9f609cb15e4fd6e93e34d Mon Sep 17 00:00:00 2001 From: Yury Semikhatsky Date: Fri, 3 Oct 2025 14:33:10 -0700 Subject: [PATCH 05/19] cherry-pick(#37703): docs: pageErrors should return strings in Java and C# --- docs/src/api/class-page.md | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/docs/src/api/class-page.md b/docs/src/api/class-page.md index 82965993c37e2..c45b55a32f66d 100644 --- a/docs/src/api/class-page.md +++ b/docs/src/api/class-page.md @@ -2687,10 +2687,18 @@ Returns up to (currently) 200 last console messages from this page. See [`event: ## async method: Page.pageErrors * since: v1.56 +* langs: js, python - returns: <[Array]<[Error]>> Returns up to (currently) 200 last page errors from this page. See [`event: Page.pageError`] for more details. +## async method: Page.pageErrors +* since: v1.56 +* langs: csharp, java +- returns: <[Array]<[string]>> + +Returns up to (currently) 200 last page errors from this page. See [`event: Page.pageError`] for more details. + ## method: Page.locator * since: v1.14 From ffbf82bbfb2dc821364be3b960e02fab7da69260 Mon Sep 17 00:00:00 2001 From: Adam Gastineau Date: Mon, 6 Oct 2025 05:33:29 -0700 Subject: [PATCH 06/19] cherry-pick(#37687): docs: v1.56 release notes (#37687) (#37720) --- docs/src/release-notes-js.md | 59 ++++++++++++++++++++++++++++++++++++ 1 file changed, 59 insertions(+) diff --git a/docs/src/release-notes-js.md b/docs/src/release-notes-js.md index 202475742065f..39f494d2b4b56 100644 --- a/docs/src/release-notes-js.md +++ b/docs/src/release-notes-js.md @@ -6,6 +6,65 @@ toc_max_heading_level: 2 import LiteYouTube from '@site/src/components/LiteYouTube'; +## Version 1.56 + + + +### Playwright Agents + +Introducing Playwright Agents, three custom agent definitions designed to guide LLMs through the core process of building a Playwright test: + +* **🎭 planner** explores the app and produces a Markdown test plan + +* **🎭 generator** transforms the Markdown plan into the Playwright Test files + +* **🎭 healer** executes the test suite and automatically repairs failing tests + +Run `npx playwright init-agents` with your client of choice to generate the latest agent definitions: + +```bash +# Generate agent files for each agentic loop +# Visual Studio Code +npx playwright init-agents --loop=vscode +# Claude Code +npx playwright init-agents --loop=claude +# opencode +npx playwright init-agents --loop=opencode +``` + +[Learn more about Playwright Agents](./test-agents.md) + +### New APIs + +- New methods [`method: Page.consoleMessages`] and [`method: Page.pageErrors`] for retrieving the most recent console messages from the page +- New method [`method: Page.requests`] for retrieving the most recent network requests from the page +- Added [`--test-list` and `--test-list-invert`](./test-cli.md#test-list) to allow manual specification of specific tests from a file + +### UI Mode and HTML Reporter + +- Added option to `'html'` reporter to disable the "Copy prompt" button +- Added option to `'html'` reporter and UI Mode to merge files, collapsing test and describe blocks into a single unified list +- Added option to UI Mode mirroring the `--update-snapshots` options +- Added option to UI Mode to run only a single worker at a time + +### Breaking Changes + +- Event [`event: BrowserContext.backgroundPage`] has been deprecated and will not be emitted. Method [`method: BrowserContext.backgroundPages`] will return an empty list + +### Miscellaneous + +- Aria snapshots render and compare `input` `placeholder` +- Added environment variable `PLAYWRIGHT_TEST` to Playwright worker processes to allow discriminating on testing status + +### Browser Versions + +- Chromium 141.0.7390.37 +- Mozilla Firefox 142.0.1 +- WebKit 26.0 + ## Version 1.55 ### New APIs From 1621d0bfcfefceb3d30a6dd656268ffddfc5cef7 Mon Sep 17 00:00:00 2001 From: Dmitry Gozman Date: Mon, 6 Oct 2025 13:51:44 +0100 Subject: [PATCH 07/19] cherry-pick(#37715): chore: disable RenderDocument feature (#37718) --- .../playwright-core/src/server/chromium/chromiumSwitches.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/packages/playwright-core/src/server/chromium/chromiumSwitches.ts b/packages/playwright-core/src/server/chromium/chromiumSwitches.ts index 5f2526c655a66..75a715aea7312 100644 --- a/packages/playwright-core/src/server/chromium/chromiumSwitches.ts +++ b/packages/playwright-core/src/server/chromium/chromiumSwitches.ts @@ -40,6 +40,8 @@ const disabledFeatures = (assistantMode?: boolean) => [ 'Translate', // See https://issues.chromium.org/u/1/issues/435410220 'AutoDeElevate', + // See https://github.com/microsoft/playwright/issues/37714 + 'RenderDocument', assistantMode ? 'AutomationControlled' : '', ].filter(Boolean); From d2ef7bbd545ced0651f749544a7729b9db007ac5 Mon Sep 17 00:00:00 2001 From: Adam Gastineau Date: Mon, 6 Oct 2025 06:18:46 -0700 Subject: [PATCH 08/19] chore: mark v1.56.0 (#37722) --- package-lock.json | 62 +++++++++---------- package.json | 2 +- .../playwright-browser-chromium/package.json | 4 +- .../playwright-browser-firefox/package.json | 4 +- .../playwright-browser-webkit/package.json | 4 +- packages/playwright-chromium/package.json | 4 +- packages/playwright-client/package.json | 2 +- packages/playwright-core/package.json | 2 +- packages/playwright-ct-core/package.json | 6 +- packages/playwright-ct-react/package.json | 4 +- packages/playwright-ct-react17/package.json | 4 +- packages/playwright-ct-svelte/package.json | 4 +- packages/playwright-ct-vue/package.json | 4 +- packages/playwright-firefox/package.json | 4 +- packages/playwright-test/package.json | 4 +- packages/playwright-webkit/package.json | 4 +- packages/playwright/package.json | 4 +- 17 files changed, 61 insertions(+), 61 deletions(-) diff --git a/package-lock.json b/package-lock.json index 03159dae8b097..5a52322f9dad5 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "playwright-internal", - "version": "1.56.0-next", + "version": "1.56.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "playwright-internal", - "version": "1.56.0-next", + "version": "1.56.0", "license": "Apache-2.0", "workspaces": [ "packages/*" @@ -8142,10 +8142,10 @@ "version": "0.0.0" }, "packages/playwright": { - "version": "1.56.0-next", + "version": "1.56.0", "license": "Apache-2.0", "dependencies": { - "playwright-core": "1.56.0-next" + "playwright-core": "1.56.0" }, "bin": { "playwright": "cli.js" @@ -8159,11 +8159,11 @@ }, "packages/playwright-browser-chromium": { "name": "@playwright/browser-chromium", - "version": "1.56.0-next", + "version": "1.56.0", "hasInstallScript": true, "license": "Apache-2.0", "dependencies": { - "playwright-core": "1.56.0-next" + "playwright-core": "1.56.0" }, "engines": { "node": ">=18" @@ -8171,11 +8171,11 @@ }, "packages/playwright-browser-firefox": { "name": "@playwright/browser-firefox", - "version": "1.56.0-next", + "version": "1.56.0", "hasInstallScript": true, "license": "Apache-2.0", "dependencies": { - "playwright-core": "1.56.0-next" + "playwright-core": "1.56.0" }, "engines": { "node": ">=18" @@ -8183,22 +8183,22 @@ }, "packages/playwright-browser-webkit": { "name": "@playwright/browser-webkit", - "version": "1.56.0-next", + "version": "1.56.0", "hasInstallScript": true, "license": "Apache-2.0", "dependencies": { - "playwright-core": "1.56.0-next" + "playwright-core": "1.56.0" }, "engines": { "node": ">=18" } }, "packages/playwright-chromium": { - "version": "1.56.0-next", + "version": "1.56.0", "hasInstallScript": true, "license": "Apache-2.0", "dependencies": { - "playwright-core": "1.56.0-next" + "playwright-core": "1.56.0" }, "bin": { "playwright": "cli.js" @@ -8212,14 +8212,14 @@ "version": "0.0.0", "license": "Apache-2.0", "dependencies": { - "playwright-core": "1.56.0-next" + "playwright-core": "1.56.0" }, "engines": { "node": ">=18" } }, "packages/playwright-core": { - "version": "1.56.0-next", + "version": "1.56.0", "license": "Apache-2.0", "bin": { "playwright-core": "cli.js" @@ -8230,11 +8230,11 @@ }, "packages/playwright-ct-core": { "name": "@playwright/experimental-ct-core", - "version": "1.56.0-next", + "version": "1.56.0", "license": "Apache-2.0", "dependencies": { - "playwright": "1.56.0-next", - "playwright-core": "1.56.0-next", + "playwright": "1.56.0", + "playwright-core": "1.56.0", "vite": "^6.3.6" }, "engines": { @@ -8243,10 +8243,10 @@ }, "packages/playwright-ct-react": { "name": "@playwright/experimental-ct-react", - "version": "1.56.0-next", + "version": "1.56.0", "license": "Apache-2.0", "dependencies": { - "@playwright/experimental-ct-core": "1.56.0-next", + "@playwright/experimental-ct-core": "1.56.0", "@vitejs/plugin-react": "^4.2.1" }, "bin": { @@ -8258,10 +8258,10 @@ }, "packages/playwright-ct-react17": { "name": "@playwright/experimental-ct-react17", - "version": "1.56.0-next", + "version": "1.56.0", "license": "Apache-2.0", "dependencies": { - "@playwright/experimental-ct-core": "1.56.0-next", + "@playwright/experimental-ct-core": "1.56.0", "@vitejs/plugin-react": "^4.2.1" }, "bin": { @@ -8273,10 +8273,10 @@ }, "packages/playwright-ct-svelte": { "name": "@playwright/experimental-ct-svelte", - "version": "1.56.0-next", + "version": "1.56.0", "license": "Apache-2.0", "dependencies": { - "@playwright/experimental-ct-core": "1.56.0-next", + "@playwright/experimental-ct-core": "1.56.0", "@sveltejs/vite-plugin-svelte": "^5.1.0" }, "bin": { @@ -8291,10 +8291,10 @@ }, "packages/playwright-ct-vue": { "name": "@playwright/experimental-ct-vue", - "version": "1.56.0-next", + "version": "1.56.0", "license": "Apache-2.0", "dependencies": { - "@playwright/experimental-ct-core": "1.56.0-next", + "@playwright/experimental-ct-core": "1.56.0", "@vitejs/plugin-vue": "^5.2.0" }, "bin": { @@ -8305,11 +8305,11 @@ } }, "packages/playwright-firefox": { - "version": "1.56.0-next", + "version": "1.56.0", "hasInstallScript": true, "license": "Apache-2.0", "dependencies": { - "playwright-core": "1.56.0-next" + "playwright-core": "1.56.0" }, "bin": { "playwright": "cli.js" @@ -8320,10 +8320,10 @@ }, "packages/playwright-test": { "name": "@playwright/test", - "version": "1.56.0-next", + "version": "1.56.0", "license": "Apache-2.0", "dependencies": { - "playwright": "1.56.0-next" + "playwright": "1.56.0" }, "bin": { "playwright": "cli.js" @@ -8333,11 +8333,11 @@ } }, "packages/playwright-webkit": { - "version": "1.56.0-next", + "version": "1.56.0", "hasInstallScript": true, "license": "Apache-2.0", "dependencies": { - "playwright-core": "1.56.0-next" + "playwright-core": "1.56.0" }, "bin": { "playwright": "cli.js" diff --git a/package.json b/package.json index 2973982979f40..706751a1a468c 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "playwright-internal", "private": true, - "version": "1.56.0-next", + "version": "1.56.0", "description": "A high-level API to automate web browsers", "repository": { "type": "git", diff --git a/packages/playwright-browser-chromium/package.json b/packages/playwright-browser-chromium/package.json index e768e2496707e..60383d54471dc 100644 --- a/packages/playwright-browser-chromium/package.json +++ b/packages/playwright-browser-chromium/package.json @@ -1,6 +1,6 @@ { "name": "@playwright/browser-chromium", - "version": "1.56.0-next", + "version": "1.56.0", "description": "Playwright package that automatically installs Chromium", "repository": { "type": "git", @@ -27,6 +27,6 @@ "install": "node install.js" }, "dependencies": { - "playwright-core": "1.56.0-next" + "playwright-core": "1.56.0" } } diff --git a/packages/playwright-browser-firefox/package.json b/packages/playwright-browser-firefox/package.json index 39702a88f0406..1aa4a0b0d0210 100644 --- a/packages/playwright-browser-firefox/package.json +++ b/packages/playwright-browser-firefox/package.json @@ -1,6 +1,6 @@ { "name": "@playwright/browser-firefox", - "version": "1.56.0-next", + "version": "1.56.0", "description": "Playwright package that automatically installs Firefox", "repository": { "type": "git", @@ -27,6 +27,6 @@ "install": "node install.js" }, "dependencies": { - "playwright-core": "1.56.0-next" + "playwright-core": "1.56.0" } } diff --git a/packages/playwright-browser-webkit/package.json b/packages/playwright-browser-webkit/package.json index 7e7a32e669753..8b4f4eee4823b 100644 --- a/packages/playwright-browser-webkit/package.json +++ b/packages/playwright-browser-webkit/package.json @@ -1,6 +1,6 @@ { "name": "@playwright/browser-webkit", - "version": "1.56.0-next", + "version": "1.56.0", "description": "Playwright package that automatically installs WebKit", "repository": { "type": "git", @@ -27,6 +27,6 @@ "install": "node install.js" }, "dependencies": { - "playwright-core": "1.56.0-next" + "playwright-core": "1.56.0" } } diff --git a/packages/playwright-chromium/package.json b/packages/playwright-chromium/package.json index 61f3fde050267..e210506db1bb9 100644 --- a/packages/playwright-chromium/package.json +++ b/packages/playwright-chromium/package.json @@ -1,6 +1,6 @@ { "name": "playwright-chromium", - "version": "1.56.0-next", + "version": "1.56.0", "description": "A high-level API to automate Chromium", "repository": { "type": "git", @@ -30,6 +30,6 @@ "install": "node install.js" }, "dependencies": { - "playwright-core": "1.56.0-next" + "playwright-core": "1.56.0" } } diff --git a/packages/playwright-client/package.json b/packages/playwright-client/package.json index 0058240e1ad0c..d27f5995a2d97 100644 --- a/packages/playwright-client/package.json +++ b/packages/playwright-client/package.json @@ -30,6 +30,6 @@ "watch": "npm run esbuild -- --watch" }, "dependencies": { - "playwright-core": "1.56.0-next" + "playwright-core": "1.56.0" } } diff --git a/packages/playwright-core/package.json b/packages/playwright-core/package.json index 5eec99ac41453..0b8fdbb9a59a1 100644 --- a/packages/playwright-core/package.json +++ b/packages/playwright-core/package.json @@ -1,6 +1,6 @@ { "name": "playwright-core", - "version": "1.56.0-next", + "version": "1.56.0", "description": "A high-level API to automate web browsers", "repository": { "type": "git", diff --git a/packages/playwright-ct-core/package.json b/packages/playwright-ct-core/package.json index 97f6ea292b86c..33fa285c5d119 100644 --- a/packages/playwright-ct-core/package.json +++ b/packages/playwright-ct-core/package.json @@ -1,6 +1,6 @@ { "name": "@playwright/experimental-ct-core", - "version": "1.56.0-next", + "version": "1.56.0", "description": "Playwright Component Testing Helpers", "repository": { "type": "git", @@ -26,8 +26,8 @@ } }, "dependencies": { - "playwright-core": "1.56.0-next", + "playwright-core": "1.56.0", "vite": "^6.3.6", - "playwright": "1.56.0-next" + "playwright": "1.56.0" } } diff --git a/packages/playwright-ct-react/package.json b/packages/playwright-ct-react/package.json index 27098cfbafc3c..924df0da6220a 100644 --- a/packages/playwright-ct-react/package.json +++ b/packages/playwright-ct-react/package.json @@ -1,6 +1,6 @@ { "name": "@playwright/experimental-ct-react", - "version": "1.56.0-next", + "version": "1.56.0", "description": "Playwright Component Testing for React", "repository": { "type": "git", @@ -30,7 +30,7 @@ "./package.json": "./package.json" }, "dependencies": { - "@playwright/experimental-ct-core": "1.56.0-next", + "@playwright/experimental-ct-core": "1.56.0", "@vitejs/plugin-react": "^4.2.1" }, "bin": { diff --git a/packages/playwright-ct-react17/package.json b/packages/playwright-ct-react17/package.json index 3754c8583958f..63319abf93d16 100644 --- a/packages/playwright-ct-react17/package.json +++ b/packages/playwright-ct-react17/package.json @@ -1,6 +1,6 @@ { "name": "@playwright/experimental-ct-react17", - "version": "1.56.0-next", + "version": "1.56.0", "description": "Playwright Component Testing for React", "repository": { "type": "git", @@ -30,7 +30,7 @@ "./package.json": "./package.json" }, "dependencies": { - "@playwright/experimental-ct-core": "1.56.0-next", + "@playwright/experimental-ct-core": "1.56.0", "@vitejs/plugin-react": "^4.2.1" }, "bin": { diff --git a/packages/playwright-ct-svelte/package.json b/packages/playwright-ct-svelte/package.json index adfca41c1ccdd..6cf95fd850470 100644 --- a/packages/playwright-ct-svelte/package.json +++ b/packages/playwright-ct-svelte/package.json @@ -1,6 +1,6 @@ { "name": "@playwright/experimental-ct-svelte", - "version": "1.56.0-next", + "version": "1.56.0", "description": "Playwright Component Testing for Svelte", "repository": { "type": "git", @@ -30,7 +30,7 @@ "./package.json": "./package.json" }, "dependencies": { - "@playwright/experimental-ct-core": "1.56.0-next", + "@playwright/experimental-ct-core": "1.56.0", "@sveltejs/vite-plugin-svelte": "^5.1.0" }, "devDependencies": { diff --git a/packages/playwright-ct-vue/package.json b/packages/playwright-ct-vue/package.json index 6beff5097dabb..5a9355af282b1 100644 --- a/packages/playwright-ct-vue/package.json +++ b/packages/playwright-ct-vue/package.json @@ -1,6 +1,6 @@ { "name": "@playwright/experimental-ct-vue", - "version": "1.56.0-next", + "version": "1.56.0", "description": "Playwright Component Testing for Vue", "repository": { "type": "git", @@ -30,7 +30,7 @@ "./package.json": "./package.json" }, "dependencies": { - "@playwright/experimental-ct-core": "1.56.0-next", + "@playwright/experimental-ct-core": "1.56.0", "@vitejs/plugin-vue": "^5.2.0" }, "bin": { diff --git a/packages/playwright-firefox/package.json b/packages/playwright-firefox/package.json index 8226a820dc6a4..8287c7c9c59c7 100644 --- a/packages/playwright-firefox/package.json +++ b/packages/playwright-firefox/package.json @@ -1,6 +1,6 @@ { "name": "playwright-firefox", - "version": "1.56.0-next", + "version": "1.56.0", "description": "A high-level API to automate Firefox", "repository": { "type": "git", @@ -30,6 +30,6 @@ "install": "node install.js" }, "dependencies": { - "playwright-core": "1.56.0-next" + "playwright-core": "1.56.0" } } diff --git a/packages/playwright-test/package.json b/packages/playwright-test/package.json index cbcef86b234a6..3effa52724bcd 100644 --- a/packages/playwright-test/package.json +++ b/packages/playwright-test/package.json @@ -1,6 +1,6 @@ { "name": "@playwright/test", - "version": "1.56.0-next", + "version": "1.56.0", "description": "A high-level API to automate web browsers", "repository": { "type": "git", @@ -30,6 +30,6 @@ }, "scripts": {}, "dependencies": { - "playwright": "1.56.0-next" + "playwright": "1.56.0" } } diff --git a/packages/playwright-webkit/package.json b/packages/playwright-webkit/package.json index 8a95b554f728f..bab3ddd8055c9 100644 --- a/packages/playwright-webkit/package.json +++ b/packages/playwright-webkit/package.json @@ -1,6 +1,6 @@ { "name": "playwright-webkit", - "version": "1.56.0-next", + "version": "1.56.0", "description": "A high-level API to automate WebKit", "repository": { "type": "git", @@ -30,6 +30,6 @@ "install": "node install.js" }, "dependencies": { - "playwright-core": "1.56.0-next" + "playwright-core": "1.56.0" } } diff --git a/packages/playwright/package.json b/packages/playwright/package.json index 0e47ef02e9ca5..1d77b4062de6a 100644 --- a/packages/playwright/package.json +++ b/packages/playwright/package.json @@ -1,6 +1,6 @@ { "name": "playwright", - "version": "1.56.0-next", + "version": "1.56.0", "description": "A high-level API to automate web browsers", "repository": { "type": "git", @@ -64,7 +64,7 @@ }, "license": "Apache-2.0", "dependencies": { - "playwright-core": "1.56.0-next" + "playwright-core": "1.56.0" }, "optionalDependencies": { "fsevents": "2.3.2" From b6af258d07383f7cce6f9f357dffd5a2d2a0be68 Mon Sep 17 00:00:00 2001 From: Adam Gastineau Date: Mon, 6 Oct 2025 07:31:49 -0700 Subject: [PATCH 09/19] cherry-pick(#37727): devops: fix NPM release step (#37728) --- .github/workflows/publish_release.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/publish_release.yml b/.github/workflows/publish_release.yml index 27dbe70a5e47c..b2f9cb2e7dcb1 100644 --- a/.github/workflows/publish_release.yml +++ b/.github/workflows/publish_release.yml @@ -50,7 +50,7 @@ jobs: node utils/build/update_canary_version.js --beta --commit-timestamp utils/publish_all_packages.sh --beta - name: "publish release to NPM" - if: contains(github.ref, 'release') && github.event_name == 'release' + if: github.event_name == 'release' && github.event.action == 'published' run: utils/publish_all_packages.sh --release - name: Azure Login From f36b2eec65df570d4ec9544e3dddc05ada84fb65 Mon Sep 17 00:00:00 2001 From: Adam Gastineau Date: Mon, 6 Oct 2025 10:27:30 -0700 Subject: [PATCH 10/19] cherry-pick(#37731): docs: add agents video to agents page (#37733) --- docs/src/test-agents-js.md | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/docs/src/test-agents-js.md b/docs/src/test-agents-js.md index b608ab7909cdb..254afc83bbe7b 100644 --- a/docs/src/test-agents-js.md +++ b/docs/src/test-agents-js.md @@ -3,8 +3,12 @@ id: test-agents title: "Agents" --- +import LiteYouTube from '@site/src/components/LiteYouTube'; + # Playwright Agents +## Introduction + Playwright comes with three Playwright Agents out of the box: **🎭 planner**, **🎭 generator** and **🎭 healer**. These agents can be used independently, sequentially, or as the chained calls in the agentic loop. @@ -16,6 +20,11 @@ Using them sequentially will produce test coverage for your product. * **🎭 healer** executes the test suite and automatically repairs failing tests + + ### Getting Started Start with adding Playwright Agent definitions to your project using From a8a6e1049bf85ad31f621dfc33d099898506c4a7 Mon Sep 17 00:00:00 2001 From: Yury Semikhatsky Date: Tue, 7 Oct 2025 11:18:29 -0700 Subject: [PATCH 11/19] cherry-pick(#37755): chore(mcp): minimal vscode version notice --- packages/playwright/src/agents/generateAgents.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/packages/playwright/src/agents/generateAgents.ts b/packages/playwright/src/agents/generateAgents.ts index 7fe10b7619193..015cc749f4ab4 100644 --- a/packages/playwright/src/agents/generateAgents.ts +++ b/packages/playwright/src/agents/generateAgents.ts @@ -16,7 +16,7 @@ import fs from 'fs'; import path from 'path'; -import { yaml } from 'playwright-core/lib/utilsBundle'; +import { colors, yaml } from 'playwright-core/lib/utilsBundle'; interface AgentHeader { name: string; @@ -280,6 +280,8 @@ export async function initVSCodeRepo() { cwd: '${workspaceFolder}', }; await writeFile(mcpJsonPath, JSON.stringify(mcpJson, null, 2)); + // eslint-disable-next-line no-console + console.log(colors.yellow(`${colors.bold('Note:')} Playwright Agents require VSCode version 1.105+ or VSCode Insiders`)); } export async function initOpencodeRepo() { From e593c64187f8d2687c4ed1b6cca44a022fee057b Mon Sep 17 00:00:00 2001 From: Pavel Feldman Date: Tue, 7 Oct 2025 12:20:34 -0700 Subject: [PATCH 12/19] cherry-pick(#37757): chore(mcp): fallback to cwd when resolving test config --- .../playwright/src/mcp/test/testBackend.ts | 2 +- tests/mcp/fixtures.ts | 18 +++++++++++------- tests/mcp/test-list.spec.ts | 18 ++++++++++++++++++ 3 files changed, 30 insertions(+), 8 deletions(-) diff --git a/packages/playwright/src/mcp/test/testBackend.ts b/packages/playwright/src/mcp/test/testBackend.ts index 3338c08b9480a..8ac54f001cc08 100644 --- a/packages/playwright/src/mcp/test/testBackend.ts +++ b/packages/playwright/src/mcp/test/testBackend.ts @@ -58,7 +58,7 @@ export class TestServerBackend implements mcp.ServerBackend { return; } - throw new Error('No config option or MCP root path provided'); + this._context.initialize(rootPath, resolveConfigLocation(undefined)); } async listTools(): Promise { diff --git a/tests/mcp/fixtures.ts b/tests/mcp/fixtures.ts index 28e296e324c90..9f55b0c64335e 100644 --- a/tests/mcp/fixtures.ts +++ b/tests/mcp/fixtures.ts @@ -48,6 +48,8 @@ type CDPServer = { export type StartClient = (options?: { clientName?: string, args?: string[], + omitArgs?: string[], + cwd?: string, config?: Config, roots?: { name: string, uri: string }[], rootsResponseDelay?: number, @@ -84,14 +86,14 @@ export const test = serverTest.extend { - const args: string[] = mcpArgs ?? []; + let args: string[] = mcpArgs ?? []; if (mcpHeadless) args.push('--headless'); if (mcpServerType === 'test-mcp') { if (!options?.args?.some(arg => arg.startsWith('--config'))) - args.push('--config', test.info().outputPath()); + args.push(`--config=${test.info().outputPath()}`); } else { if (process.env.CI && process.platform === 'linux') args.push('--no-sandbox'); @@ -106,6 +108,8 @@ export const test = serverTest.extend !options.omitArgs?.includes(arg)); const client = new Client({ name: options?.clientName ?? 'test', version: '1.0.0' }, options?.roots ? { capabilities: { roots: {} } } : undefined); if (options?.roots) { @@ -118,7 +122,7 @@ export const test = serverTest.extend { if (process.env.PWDEBUGIMPL) @@ -182,18 +186,18 @@ export const test = serverTest.extend { const profilesDir = test.info().outputPath('ms-playwright'); const transport = new StdioClientTransport({ command: 'node', - args: [...(mcpServerType === 'test-mcp' ? testMcpServerPath : mcpServerPath), ...args], - cwd: test.info().outputPath(), + args: [...(mcpServerType === 'test-mcp' ? testMcpServerPath : mcpServerPath), ...options.args], + cwd: options.cwd, stderr: 'pipe', env: { - ...env, + ...options.env, DEBUG_COLORS: '0', DEBUG_HIDE_DATE: '1', PWMCP_PROFILES_DIR_FOR_TEST: profilesDir, diff --git a/tests/mcp/test-list.spec.ts b/tests/mcp/test-list.spec.ts index af94d470b8117..561e9bea844ab 100644 --- a/tests/mcp/test-list.spec.ts +++ b/tests/mcp/test-list.spec.ts @@ -45,3 +45,21 @@ test('test_list', async ({ startClient }) => { [id=] a.test.ts:6:11 › example2 Total: 4 tests in 1 file`); }); + +test('test_list config in cwd', async ({ startClient }) => { + await writeFiles({ + 'a.test.ts': ` + import { test, expect } from '@playwright/test'; + test('passes', () => {}); + `, + }); + + const { client } = await startClient({ + omitArgs: ['--config'], + }); + expect(await client.callTool({ + name: 'test_list', + })).toHaveTextResponse(`Listing tests: + [id=] a.test.ts:3:11 › passes +Total: 1 test in 1 file`); +}); From 919549ec2c3d70fad0e85fc9f86fabd6a7b7c2c8 Mon Sep 17 00:00:00 2001 From: Pavel Feldman Date: Tue, 7 Oct 2025 12:57:42 -0700 Subject: [PATCH 13/19] cherry-pick(#37758): docs: mention VS Code insiders in the agents docs --- docs/src/test-agents-js.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/docs/src/test-agents-js.md b/docs/src/test-agents-js.md index 254afc83bbe7b..28e65b361b013 100644 --- a/docs/src/test-agents-js.md +++ b/docs/src/test-agents-js.md @@ -43,6 +43,10 @@ npx playwright init-agents --loop=claude npx playwright init-agents --loop=opencode ``` +:::note +VS Code v1.105 (currently on the [VS Code Insiders channel](https://code.visualstudio.com/insiders/)) is needed for the agentic experience in VS Code. It will become stable shortly, we are a bit ahead of the curve with this functionality! +::: + Once the agents have been generated, you can use your AI tool of choice to command these agents to build Playwright Tests. From 0662dd29eed5df12d09bc3c871ac2164a4f62969 Mon Sep 17 00:00:00 2001 From: Pavel Feldman Date: Tue, 7 Oct 2025 13:23:37 -0700 Subject: [PATCH 14/19] cherry-pick(#37759): chore: rename agents to test agents --- docs/src/release-notes-js.md | 8 ++++---- docs/src/test-agents-js.md | 8 ++++---- packages/playwright/src/agents/generateAgents.ts | 2 +- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/docs/src/release-notes-js.md b/docs/src/release-notes-js.md index 39f494d2b4b56..7c05fe47197cc 100644 --- a/docs/src/release-notes-js.md +++ b/docs/src/release-notes-js.md @@ -10,12 +10,12 @@ import LiteYouTube from '@site/src/components/LiteYouTube'; -### Playwright Agents +### Playwright Test Agents -Introducing Playwright Agents, three custom agent definitions designed to guide LLMs through the core process of building a Playwright test: +Introducing Playwright Test Agents, three custom agent definitions designed to guide LLMs through the core process of building a Playwright test: * **🎭 planner** explores the app and produces a Markdown test plan @@ -35,7 +35,7 @@ npx playwright init-agents --loop=claude npx playwright init-agents --loop=opencode ``` -[Learn more about Playwright Agents](./test-agents.md) +[Learn more about Playwright Test Agents](./test-agents.md) ### New APIs diff --git a/docs/src/test-agents-js.md b/docs/src/test-agents-js.md index 28e65b361b013..e19faad567ba7 100644 --- a/docs/src/test-agents-js.md +++ b/docs/src/test-agents-js.md @@ -5,11 +5,11 @@ title: "Agents" import LiteYouTube from '@site/src/components/LiteYouTube'; -# Playwright Agents +# Playwright Test Agents ## Introduction -Playwright comes with three Playwright Agents out of the box: **🎭 planner**, **🎭 generator** and **🎭 healer**. +Playwright comes with three Playwright Test Agents out of the box: **🎭 planner**, **🎭 generator** and **🎭 healer**. These agents can be used independently, sequentially, or as the chained calls in the agentic loop. Using them sequentially will produce test coverage for your product. @@ -22,12 +22,12 @@ Using them sequentially will produce test coverage for your product. ### Getting Started -Start with adding Playwright Agent definitions to your project using +Start with adding Playwright Test Agent definitions to your project using the `init-agents` command. These definitions should be regenerated whenever Playwright is updated to pick up new tools and instructions. diff --git a/packages/playwright/src/agents/generateAgents.ts b/packages/playwright/src/agents/generateAgents.ts index 015cc749f4ab4..efa0ed0802015 100644 --- a/packages/playwright/src/agents/generateAgents.ts +++ b/packages/playwright/src/agents/generateAgents.ts @@ -281,7 +281,7 @@ export async function initVSCodeRepo() { }; await writeFile(mcpJsonPath, JSON.stringify(mcpJson, null, 2)); // eslint-disable-next-line no-console - console.log(colors.yellow(`${colors.bold('Note:')} Playwright Agents require VSCode version 1.105+ or VSCode Insiders`)); + console.log(colors.yellow(`${colors.bold('Note:')} Playwright Test Agents require VSCode version 1.105+ or VSCode Insiders`)); } export async function initOpencodeRepo() { From 932542c3c1e8c864bfbd48ecf38a55098d703703 Mon Sep 17 00:00:00 2001 From: Pavel Feldman Date: Thu, 16 Oct 2025 15:08:50 -0700 Subject: [PATCH 15/19] cherry-pick(#37891): fix(agents): remove workspaceFolder ref from vscode mcp --- packages/playwright/src/agents/generateAgents.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/playwright/src/agents/generateAgents.ts b/packages/playwright/src/agents/generateAgents.ts index efa0ed0802015..3a2f5331669ea 100644 --- a/packages/playwright/src/agents/generateAgents.ts +++ b/packages/playwright/src/agents/generateAgents.ts @@ -277,7 +277,6 @@ export async function initVSCodeRepo() { type: 'stdio', command: commonMcpServers.playwrightTest.command, args: commonMcpServers.playwrightTest.args, - cwd: '${workspaceFolder}', }; await writeFile(mcpJsonPath, JSON.stringify(mcpJson, null, 2)); // eslint-disable-next-line no-console From e6ef6974bedb32d15d1e525a16caf3a95c1a7173 Mon Sep 17 00:00:00 2001 From: Yury Semikhatsky Date: Thu, 16 Oct 2025 16:59:48 -0700 Subject: [PATCH 16/19] cherry-pick(#37871): chore: allow local-network-access permission in chromium --- docs/src/api/class-browsercontext.md | 3 +- packages/playwright-client/types/types.d.ts | 3 +- .../src/server/chromium/crBrowser.ts | 1 + packages/playwright-core/types/types.d.ts | 3 +- tests/library/permissions.spec.ts | 48 +++++++++++++++++++ 5 files changed, 55 insertions(+), 3 deletions(-) diff --git a/docs/src/api/class-browsercontext.md b/docs/src/api/class-browsercontext.md index 97c9303225d13..d0b5a23060178 100644 --- a/docs/src/api/class-browsercontext.md +++ b/docs/src/api/class-browsercontext.md @@ -948,6 +948,8 @@ Here are some permissions that may be supported by some browsers: * `'clipboard-write'` * `'geolocation'` * `'gyroscope'` +* `'local-fonts'` +* `'local-network-access'` * `'magnetometer'` * `'microphone'` * `'midi-sysex'` (system-exclusive midi) @@ -955,7 +957,6 @@ Here are some permissions that may be supported by some browsers: * `'notifications'` * `'payment-handler'` * `'storage-access'` -* `'local-fonts'` ### option: BrowserContext.grantPermissions.origin * since: v1.8 diff --git a/packages/playwright-client/types/types.d.ts b/packages/playwright-client/types/types.d.ts index d4e4f52c74f85..b7d8f0083433d 100644 --- a/packages/playwright-client/types/types.d.ts +++ b/packages/playwright-client/types/types.d.ts @@ -8991,6 +8991,8 @@ export interface BrowserContext { * - `'clipboard-write'` * - `'geolocation'` * - `'gyroscope'` + * - `'local-fonts'` + * - `'local-network-access'` * - `'magnetometer'` * - `'microphone'` * - `'midi-sysex'` (system-exclusive midi) @@ -8998,7 +9000,6 @@ export interface BrowserContext { * - `'notifications'` * - `'payment-handler'` * - `'storage-access'` - * - `'local-fonts'` * @param options */ grantPermissions(permissions: ReadonlyArray, options?: { diff --git a/packages/playwright-core/src/server/chromium/crBrowser.ts b/packages/playwright-core/src/server/chromium/crBrowser.ts index ef6f16e7c84a2..a81e3d88b9beb 100644 --- a/packages/playwright-core/src/server/chromium/crBrowser.ts +++ b/packages/playwright-core/src/server/chromium/crBrowser.ts @@ -435,6 +435,7 @@ export class CRBrowserContext extends BrowserContext { ['midi-sysex', 'midiSysex'], ['storage-access', 'storageAccess'], ['local-fonts', 'localFonts'], + ['local-network-access', 'localNetworkAccess'], ]); const filtered = permissions.map(permission => { const protocolPermission = webPermissionToProtocol.get(permission); diff --git a/packages/playwright-core/types/types.d.ts b/packages/playwright-core/types/types.d.ts index d4e4f52c74f85..b7d8f0083433d 100644 --- a/packages/playwright-core/types/types.d.ts +++ b/packages/playwright-core/types/types.d.ts @@ -8991,6 +8991,8 @@ export interface BrowserContext { * - `'clipboard-write'` * - `'geolocation'` * - `'gyroscope'` + * - `'local-fonts'` + * - `'local-network-access'` * - `'magnetometer'` * - `'microphone'` * - `'midi-sysex'` (system-exclusive midi) @@ -8998,7 +9000,6 @@ export interface BrowserContext { * - `'notifications'` * - `'payment-handler'` * - `'storage-access'` - * - `'local-fonts'` * @param options */ grantPermissions(permissions: ReadonlyArray, options?: { diff --git a/tests/library/permissions.spec.ts b/tests/library/permissions.spec.ts index b723aee813971..f753adc794562 100644 --- a/tests/library/permissions.spec.ts +++ b/tests/library/permissions.spec.ts @@ -253,3 +253,51 @@ it.describe(() => { expect(await page.evaluate(async () => (await (window as any).queryLocalFonts()).length > 0)).toBe(true); }); }); + +it('local network request is allowed from public origin', { + annotation: { type: 'issue', description: 'https://github.com/microsoft/playwright/issues/37861' } +}, async ({ page, context, server, browserName }) => { + it.fail(browserName === 'webkit'); + if (browserName === 'chromium') + await context.grantPermissions(['local-network-access']); + const serverRequests = []; + server.setRoute('/cors', (req, res) => { + serverRequests.push(`${req.method} ${req.url}`); + if (req.method === 'OPTIONS') { + res.writeHead(204, { + 'Access-Control-Allow-Origin': '*', + 'Access-Control-Allow-Methods': 'GET, POST, PUT, OPTIONS', + 'Access-Control-Allow-Headers': '*', + }); + res.end(); + return; + } + res.writeHead(200, { 'Content-type': 'text/plain', 'Access-Control-Allow-Origin': '*' }); + res.end('Hello there!'); + }); + const clientRequests = []; + // Has to be a public origin. + await page.goto('https://demo.playwright.dev/todomvc/'); + page.on('request', request => { + clientRequests.push(`${request.method()} ${request.url()}`); + }); + const response = await page.evaluate(async url => { + const response = await fetch(url, { + method: 'POST', + body: '', + headers: { + 'Content-Type': 'application/json', + 'X-Custom-Header': 'test-value' + } + }); + return await response.text(); + }, server.CROSS_PROCESS_PREFIX + '/cors').catch(e => e.message); + expect(response).toBe('Hello there!'); + expect(serverRequests).toEqual([ + 'OPTIONS /cors', + 'POST /cors', + ]); + expect(clientRequests).toEqual([ + `POST ${server.CROSS_PROCESS_PREFIX}/cors`, + ]); +}); From 7d45eb331a6bac304fb8640129e0931192ad7e93 Mon Sep 17 00:00:00 2001 From: Yury Semikhatsky Date: Thu, 16 Oct 2025 17:02:00 -0700 Subject: [PATCH 17/19] chore: mark v1.56.1 (#37784) --- package-lock.json | 62 +++++++++---------- package.json | 2 +- .../playwright-browser-chromium/package.json | 4 +- .../playwright-browser-firefox/package.json | 4 +- .../playwright-browser-webkit/package.json | 4 +- packages/playwright-chromium/package.json | 4 +- packages/playwright-client/package.json | 2 +- packages/playwright-core/package.json | 2 +- packages/playwright-ct-core/package.json | 6 +- packages/playwright-ct-react/package.json | 4 +- packages/playwright-ct-react17/package.json | 4 +- packages/playwright-ct-svelte/package.json | 4 +- packages/playwright-ct-vue/package.json | 4 +- packages/playwright-firefox/package.json | 4 +- packages/playwright-test/package.json | 4 +- packages/playwright-webkit/package.json | 4 +- packages/playwright/package.json | 4 +- 17 files changed, 61 insertions(+), 61 deletions(-) diff --git a/package-lock.json b/package-lock.json index 5a52322f9dad5..92eba9e27abec 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "playwright-internal", - "version": "1.56.0", + "version": "1.56.1", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "playwright-internal", - "version": "1.56.0", + "version": "1.56.1", "license": "Apache-2.0", "workspaces": [ "packages/*" @@ -8142,10 +8142,10 @@ "version": "0.0.0" }, "packages/playwright": { - "version": "1.56.0", + "version": "1.56.1", "license": "Apache-2.0", "dependencies": { - "playwright-core": "1.56.0" + "playwright-core": "1.56.1" }, "bin": { "playwright": "cli.js" @@ -8159,11 +8159,11 @@ }, "packages/playwright-browser-chromium": { "name": "@playwright/browser-chromium", - "version": "1.56.0", + "version": "1.56.1", "hasInstallScript": true, "license": "Apache-2.0", "dependencies": { - "playwright-core": "1.56.0" + "playwright-core": "1.56.1" }, "engines": { "node": ">=18" @@ -8171,11 +8171,11 @@ }, "packages/playwright-browser-firefox": { "name": "@playwright/browser-firefox", - "version": "1.56.0", + "version": "1.56.1", "hasInstallScript": true, "license": "Apache-2.0", "dependencies": { - "playwright-core": "1.56.0" + "playwright-core": "1.56.1" }, "engines": { "node": ">=18" @@ -8183,22 +8183,22 @@ }, "packages/playwright-browser-webkit": { "name": "@playwright/browser-webkit", - "version": "1.56.0", + "version": "1.56.1", "hasInstallScript": true, "license": "Apache-2.0", "dependencies": { - "playwright-core": "1.56.0" + "playwright-core": "1.56.1" }, "engines": { "node": ">=18" } }, "packages/playwright-chromium": { - "version": "1.56.0", + "version": "1.56.1", "hasInstallScript": true, "license": "Apache-2.0", "dependencies": { - "playwright-core": "1.56.0" + "playwright-core": "1.56.1" }, "bin": { "playwright": "cli.js" @@ -8212,14 +8212,14 @@ "version": "0.0.0", "license": "Apache-2.0", "dependencies": { - "playwright-core": "1.56.0" + "playwright-core": "1.56.1" }, "engines": { "node": ">=18" } }, "packages/playwright-core": { - "version": "1.56.0", + "version": "1.56.1", "license": "Apache-2.0", "bin": { "playwright-core": "cli.js" @@ -8230,11 +8230,11 @@ }, "packages/playwright-ct-core": { "name": "@playwright/experimental-ct-core", - "version": "1.56.0", + "version": "1.56.1", "license": "Apache-2.0", "dependencies": { - "playwright": "1.56.0", - "playwright-core": "1.56.0", + "playwright": "1.56.1", + "playwright-core": "1.56.1", "vite": "^6.3.6" }, "engines": { @@ -8243,10 +8243,10 @@ }, "packages/playwright-ct-react": { "name": "@playwright/experimental-ct-react", - "version": "1.56.0", + "version": "1.56.1", "license": "Apache-2.0", "dependencies": { - "@playwright/experimental-ct-core": "1.56.0", + "@playwright/experimental-ct-core": "1.56.1", "@vitejs/plugin-react": "^4.2.1" }, "bin": { @@ -8258,10 +8258,10 @@ }, "packages/playwright-ct-react17": { "name": "@playwright/experimental-ct-react17", - "version": "1.56.0", + "version": "1.56.1", "license": "Apache-2.0", "dependencies": { - "@playwright/experimental-ct-core": "1.56.0", + "@playwright/experimental-ct-core": "1.56.1", "@vitejs/plugin-react": "^4.2.1" }, "bin": { @@ -8273,10 +8273,10 @@ }, "packages/playwright-ct-svelte": { "name": "@playwright/experimental-ct-svelte", - "version": "1.56.0", + "version": "1.56.1", "license": "Apache-2.0", "dependencies": { - "@playwright/experimental-ct-core": "1.56.0", + "@playwright/experimental-ct-core": "1.56.1", "@sveltejs/vite-plugin-svelte": "^5.1.0" }, "bin": { @@ -8291,10 +8291,10 @@ }, "packages/playwright-ct-vue": { "name": "@playwright/experimental-ct-vue", - "version": "1.56.0", + "version": "1.56.1", "license": "Apache-2.0", "dependencies": { - "@playwright/experimental-ct-core": "1.56.0", + "@playwright/experimental-ct-core": "1.56.1", "@vitejs/plugin-vue": "^5.2.0" }, "bin": { @@ -8305,11 +8305,11 @@ } }, "packages/playwright-firefox": { - "version": "1.56.0", + "version": "1.56.1", "hasInstallScript": true, "license": "Apache-2.0", "dependencies": { - "playwright-core": "1.56.0" + "playwright-core": "1.56.1" }, "bin": { "playwright": "cli.js" @@ -8320,10 +8320,10 @@ }, "packages/playwright-test": { "name": "@playwright/test", - "version": "1.56.0", + "version": "1.56.1", "license": "Apache-2.0", "dependencies": { - "playwright": "1.56.0" + "playwright": "1.56.1" }, "bin": { "playwright": "cli.js" @@ -8333,11 +8333,11 @@ } }, "packages/playwright-webkit": { - "version": "1.56.0", + "version": "1.56.1", "hasInstallScript": true, "license": "Apache-2.0", "dependencies": { - "playwright-core": "1.56.0" + "playwright-core": "1.56.1" }, "bin": { "playwright": "cli.js" diff --git a/package.json b/package.json index 706751a1a468c..0c2c888931ab9 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "playwright-internal", "private": true, - "version": "1.56.0", + "version": "1.56.1", "description": "A high-level API to automate web browsers", "repository": { "type": "git", diff --git a/packages/playwright-browser-chromium/package.json b/packages/playwright-browser-chromium/package.json index 60383d54471dc..bd7d2dd93ef2f 100644 --- a/packages/playwright-browser-chromium/package.json +++ b/packages/playwright-browser-chromium/package.json @@ -1,6 +1,6 @@ { "name": "@playwright/browser-chromium", - "version": "1.56.0", + "version": "1.56.1", "description": "Playwright package that automatically installs Chromium", "repository": { "type": "git", @@ -27,6 +27,6 @@ "install": "node install.js" }, "dependencies": { - "playwright-core": "1.56.0" + "playwright-core": "1.56.1" } } diff --git a/packages/playwright-browser-firefox/package.json b/packages/playwright-browser-firefox/package.json index 1aa4a0b0d0210..0d6bbdd35d196 100644 --- a/packages/playwright-browser-firefox/package.json +++ b/packages/playwright-browser-firefox/package.json @@ -1,6 +1,6 @@ { "name": "@playwright/browser-firefox", - "version": "1.56.0", + "version": "1.56.1", "description": "Playwright package that automatically installs Firefox", "repository": { "type": "git", @@ -27,6 +27,6 @@ "install": "node install.js" }, "dependencies": { - "playwright-core": "1.56.0" + "playwright-core": "1.56.1" } } diff --git a/packages/playwright-browser-webkit/package.json b/packages/playwright-browser-webkit/package.json index 8b4f4eee4823b..80cd535945d35 100644 --- a/packages/playwright-browser-webkit/package.json +++ b/packages/playwright-browser-webkit/package.json @@ -1,6 +1,6 @@ { "name": "@playwright/browser-webkit", - "version": "1.56.0", + "version": "1.56.1", "description": "Playwright package that automatically installs WebKit", "repository": { "type": "git", @@ -27,6 +27,6 @@ "install": "node install.js" }, "dependencies": { - "playwright-core": "1.56.0" + "playwright-core": "1.56.1" } } diff --git a/packages/playwright-chromium/package.json b/packages/playwright-chromium/package.json index e210506db1bb9..cd840690ff7a8 100644 --- a/packages/playwright-chromium/package.json +++ b/packages/playwright-chromium/package.json @@ -1,6 +1,6 @@ { "name": "playwright-chromium", - "version": "1.56.0", + "version": "1.56.1", "description": "A high-level API to automate Chromium", "repository": { "type": "git", @@ -30,6 +30,6 @@ "install": "node install.js" }, "dependencies": { - "playwright-core": "1.56.0" + "playwright-core": "1.56.1" } } diff --git a/packages/playwright-client/package.json b/packages/playwright-client/package.json index d27f5995a2d97..e9dbff8350ac3 100644 --- a/packages/playwright-client/package.json +++ b/packages/playwright-client/package.json @@ -30,6 +30,6 @@ "watch": "npm run esbuild -- --watch" }, "dependencies": { - "playwright-core": "1.56.0" + "playwright-core": "1.56.1" } } diff --git a/packages/playwright-core/package.json b/packages/playwright-core/package.json index 0b8fdbb9a59a1..84840cbf1c034 100644 --- a/packages/playwright-core/package.json +++ b/packages/playwright-core/package.json @@ -1,6 +1,6 @@ { "name": "playwright-core", - "version": "1.56.0", + "version": "1.56.1", "description": "A high-level API to automate web browsers", "repository": { "type": "git", diff --git a/packages/playwright-ct-core/package.json b/packages/playwright-ct-core/package.json index 33fa285c5d119..d33f3d5b082dd 100644 --- a/packages/playwright-ct-core/package.json +++ b/packages/playwright-ct-core/package.json @@ -1,6 +1,6 @@ { "name": "@playwright/experimental-ct-core", - "version": "1.56.0", + "version": "1.56.1", "description": "Playwright Component Testing Helpers", "repository": { "type": "git", @@ -26,8 +26,8 @@ } }, "dependencies": { - "playwright-core": "1.56.0", + "playwright-core": "1.56.1", "vite": "^6.3.6", - "playwright": "1.56.0" + "playwright": "1.56.1" } } diff --git a/packages/playwright-ct-react/package.json b/packages/playwright-ct-react/package.json index 924df0da6220a..3e2704f993e1d 100644 --- a/packages/playwright-ct-react/package.json +++ b/packages/playwright-ct-react/package.json @@ -1,6 +1,6 @@ { "name": "@playwright/experimental-ct-react", - "version": "1.56.0", + "version": "1.56.1", "description": "Playwright Component Testing for React", "repository": { "type": "git", @@ -30,7 +30,7 @@ "./package.json": "./package.json" }, "dependencies": { - "@playwright/experimental-ct-core": "1.56.0", + "@playwright/experimental-ct-core": "1.56.1", "@vitejs/plugin-react": "^4.2.1" }, "bin": { diff --git a/packages/playwright-ct-react17/package.json b/packages/playwright-ct-react17/package.json index 63319abf93d16..197801a75ca54 100644 --- a/packages/playwright-ct-react17/package.json +++ b/packages/playwright-ct-react17/package.json @@ -1,6 +1,6 @@ { "name": "@playwright/experimental-ct-react17", - "version": "1.56.0", + "version": "1.56.1", "description": "Playwright Component Testing for React", "repository": { "type": "git", @@ -30,7 +30,7 @@ "./package.json": "./package.json" }, "dependencies": { - "@playwright/experimental-ct-core": "1.56.0", + "@playwright/experimental-ct-core": "1.56.1", "@vitejs/plugin-react": "^4.2.1" }, "bin": { diff --git a/packages/playwright-ct-svelte/package.json b/packages/playwright-ct-svelte/package.json index 6cf95fd850470..67ca761db969f 100644 --- a/packages/playwright-ct-svelte/package.json +++ b/packages/playwright-ct-svelte/package.json @@ -1,6 +1,6 @@ { "name": "@playwright/experimental-ct-svelte", - "version": "1.56.0", + "version": "1.56.1", "description": "Playwright Component Testing for Svelte", "repository": { "type": "git", @@ -30,7 +30,7 @@ "./package.json": "./package.json" }, "dependencies": { - "@playwright/experimental-ct-core": "1.56.0", + "@playwright/experimental-ct-core": "1.56.1", "@sveltejs/vite-plugin-svelte": "^5.1.0" }, "devDependencies": { diff --git a/packages/playwright-ct-vue/package.json b/packages/playwright-ct-vue/package.json index 5a9355af282b1..1043eb689de3b 100644 --- a/packages/playwright-ct-vue/package.json +++ b/packages/playwright-ct-vue/package.json @@ -1,6 +1,6 @@ { "name": "@playwright/experimental-ct-vue", - "version": "1.56.0", + "version": "1.56.1", "description": "Playwright Component Testing for Vue", "repository": { "type": "git", @@ -30,7 +30,7 @@ "./package.json": "./package.json" }, "dependencies": { - "@playwright/experimental-ct-core": "1.56.0", + "@playwright/experimental-ct-core": "1.56.1", "@vitejs/plugin-vue": "^5.2.0" }, "bin": { diff --git a/packages/playwright-firefox/package.json b/packages/playwright-firefox/package.json index 8287c7c9c59c7..8359a7bf4615a 100644 --- a/packages/playwright-firefox/package.json +++ b/packages/playwright-firefox/package.json @@ -1,6 +1,6 @@ { "name": "playwright-firefox", - "version": "1.56.0", + "version": "1.56.1", "description": "A high-level API to automate Firefox", "repository": { "type": "git", @@ -30,6 +30,6 @@ "install": "node install.js" }, "dependencies": { - "playwright-core": "1.56.0" + "playwright-core": "1.56.1" } } diff --git a/packages/playwright-test/package.json b/packages/playwright-test/package.json index 3effa52724bcd..8342893d8aae1 100644 --- a/packages/playwright-test/package.json +++ b/packages/playwright-test/package.json @@ -1,6 +1,6 @@ { "name": "@playwright/test", - "version": "1.56.0", + "version": "1.56.1", "description": "A high-level API to automate web browsers", "repository": { "type": "git", @@ -30,6 +30,6 @@ }, "scripts": {}, "dependencies": { - "playwright": "1.56.0" + "playwright": "1.56.1" } } diff --git a/packages/playwright-webkit/package.json b/packages/playwright-webkit/package.json index bab3ddd8055c9..4dc00ad06e2f8 100644 --- a/packages/playwright-webkit/package.json +++ b/packages/playwright-webkit/package.json @@ -1,6 +1,6 @@ { "name": "playwright-webkit", - "version": "1.56.0", + "version": "1.56.1", "description": "A high-level API to automate WebKit", "repository": { "type": "git", @@ -30,6 +30,6 @@ "install": "node install.js" }, "dependencies": { - "playwright-core": "1.56.0" + "playwright-core": "1.56.1" } } diff --git a/packages/playwright/package.json b/packages/playwright/package.json index 1d77b4062de6a..b8e96d721f140 100644 --- a/packages/playwright/package.json +++ b/packages/playwright/package.json @@ -1,6 +1,6 @@ { "name": "playwright", - "version": "1.56.0", + "version": "1.56.1", "description": "A high-level API to automate web browsers", "repository": { "type": "git", @@ -64,7 +64,7 @@ }, "license": "Apache-2.0", "dependencies": { - "playwright-core": "1.56.0" + "playwright-core": "1.56.1" }, "optionalDependencies": { "fsevents": "2.3.2" From 54c711571a37de525377e6f3d3608c3e029b1829 Mon Sep 17 00:00:00 2001 From: Yury Semikhatsky Date: Thu, 16 Oct 2025 17:48:28 -0700 Subject: [PATCH 18/19] chore: revert "minimal vscode version notice" (#37892) --- packages/playwright/src/agents/generateAgents.ts | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/packages/playwright/src/agents/generateAgents.ts b/packages/playwright/src/agents/generateAgents.ts index 3a2f5331669ea..cedba6032d4a5 100644 --- a/packages/playwright/src/agents/generateAgents.ts +++ b/packages/playwright/src/agents/generateAgents.ts @@ -16,7 +16,7 @@ import fs from 'fs'; import path from 'path'; -import { colors, yaml } from 'playwright-core/lib/utilsBundle'; +import { yaml } from 'playwright-core/lib/utilsBundle'; interface AgentHeader { name: string; @@ -279,8 +279,6 @@ export async function initVSCodeRepo() { args: commonMcpServers.playwrightTest.args, }; await writeFile(mcpJsonPath, JSON.stringify(mcpJson, null, 2)); - // eslint-disable-next-line no-console - console.log(colors.yellow(`${colors.bold('Note:')} Playwright Test Agents require VSCode version 1.105+ or VSCode Insiders`)); } export async function initOpencodeRepo() { From 5cbfa844b4b13c316d20616f87d1c20ee1901642 Mon Sep 17 00:00:00 2001 From: Yury Semikhatsky Date: Tue, 21 Oct 2025 15:02:02 -0700 Subject: [PATCH 19/19] cherry-pick(#37956): devops: allow to trigger trace viewer deployment manually --- .github/workflows/publish_release.yml | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/.github/workflows/publish_release.yml b/.github/workflows/publish_release.yml index b2f9cb2e7dcb1..1bca7f6879f4b 100644 --- a/.github/workflows/publish_release.yml +++ b/.github/workflows/publish_release.yml @@ -82,17 +82,22 @@ jobs: private-key: ${{ secrets.PLAYWRIGHT_PRIVATE_KEY }} repositories: trace.playwright.dev - name: Deploy Canary + if: contains(github.ref, 'main') && github.event.schedule run: bash utils/build/deploy-trace-viewer.sh --canary - if: contains(github.ref, 'main') env: GH_SERVICE_ACCOUNT_TOKEN: ${{ steps.app-token.outputs.token }} - name: Deploy Beta - run: bash utils/build/deploy-trace-viewer.sh --beta if: contains(github.ref, 'release') && github.event_name == 'push' + run: bash utils/build/deploy-trace-viewer.sh --beta env: GH_SERVICE_ACCOUNT_TOKEN: ${{ steps.app-token.outputs.token }} - name: Deploy Stable + if: github.event_name == 'release' && github.event.action == 'published' + run: bash utils/build/deploy-trace-viewer.sh --stable + env: + GH_SERVICE_ACCOUNT_TOKEN: ${{ steps.app-token.outputs.token }} + - name: Deploy Stable (manually) + if: contains(github.ref, 'release') && github.event_name == 'workflow_dispatch' run: bash utils/build/deploy-trace-viewer.sh --stable - if: contains(github.ref, 'release') && github.event_name == 'release' env: GH_SERVICE_ACCOUNT_TOKEN: ${{ steps.app-token.outputs.token }}