Playwright 1.64: WebMCP, videos y control de proyectos
Playwright 1.64 permite testear las herramientas que una página registra vía WebMCP, mejora la grabación de video con fps y CSS personalizable, y da más control sobre qué proyectos corren por defecto y cómo se combinan locators.
WebMCP: testear las herramientas que expone tu página
WebMCP es una API experimental de navegador que permite que una página registre herramientas, de forma similar a como un servidor MCP expone tools a un agente. Antes no tenías forma de verificar esas herramientas desde Playwright sin montar tu propio cliente MCP. Ahora page.webmcp y frame.webmcp te dan acceso directo: puedes listar las tools registradas con sus nombres y descripciones, y llamarlas con callTool pasando los parámetros que esperan. Esto te sirve para escribir tests que validen la lógica de esas herramientas igual que validarías cualquier otra parte de tu app, sin depender de que un agente las invoque primero. Playwright MCP también las soporta por defecto, exponiéndolas al agente con el prefijo webmcp_, y puedes desactivar esto con --no-webmcp si no lo necesitas. playwright-cli suma los comandos webmcp-list y webmcp-call para inspeccionarlas y probarlas desde la terminal.
const browser = await chromium.launch({ args: ['--enable-features=WebMCP'] });const page = await browser.newPage();await page.goto('https://example.com');for (const tool of await page.webmcp.tools()) console.log(tool.name, tool.description);const result = await page.webmcp.callTool('add', { a: 2, b: 40 });console.log(result.content[0].text); // "42"Videos con fps configurable, CSS propio y VP9
Hasta ahora la grabación de video tenía un frame rate fijo y las decoraciones de acciones (el punto del cursor, el resaltado, el título) se controlaban con una sola opción de tamaño de fuente. Eso limitaba qué tan legibles o personalizables quedaban los videos para debugging o documentación. Con 1.64 puedes fijar fps directamente en testOptions.video, recordVideo o screencast.start(), aunque Firefox y WebKit siguen topados en 25 cuadros por segundo. La opción style reemplaza a fontSize y acepta declaraciones CSS completas para el marcador point, el highlight del elemento objetivo y el title de la acción, así que tienes control total sobre cómo se ven esas marcas. Además el cursor ahora se queda visible en el último punto de acción, sobrevive a navegaciones y se mueve con una trayectoria suavizada en vez de saltar. Por debajo, los videos se codifican en VP9 en lugar de VP8, lo que reduce el uso de CPU y entrega archivos más chicos con igual o mejor calidad. Todas estas opciones también están disponibles en Playwright MCP y playwright-cli, no solo en el test runner.
// playwright.config.tsimport { defineConfig } from '@playwright/test';export default defineConfig({ use: { video: { mode: 'on', size: { width: 1920, height: 1080 }, fps: 60, show: { actions: { style: { point: 'width: 20px; height: 20px; border-radius: 50%; background: red', highlight: 'outline: 2px solid #333; background: rgba(0, 128, 255, .15)', title: 'font-size: 16px', }, }, }, }, },});Proyectos que no corren por defecto
Si tu config define varios proyectos (chromium, firefox, webkit), hasta ahora npx playwright test sin flags los corría todos, aunque tú solo quisieras validar uno en local y dejar el resto para CI. La alternativa era mantener configs separadas o pasar --project cada vez. testProject.default resuelve esto: marcas un proyecto con default: false y queda configurado en el archivo, pero npx playwright test sin argumentos lo salta. Sigues pudiendo invocarlo explícitamente con --project=firefox, y --project="*" corre todos los proyectos definidos, el patrón típico para un job de CI que sí necesita cobertura completa en los tres motores.
// playwright.config.tsimport { defineConfig, devices } from '@playwright/test';export default defineConfig({ projects: [ { name: 'chromium', use: devices['Desktop Chrome'] }, { name: 'firefox', use: devices['Desktop Firefox'], default: false }, { name: 'webkit', use: devices['Desktop Safari'], default: false }, ],});Orden aleatorio y locks entre tests
Dos herramientas nuevas apuntan a un mismo problema: tests que dependen implícitamente del orden en que corren o de recursos compartidos. --shuffle ejecuta los tests en orden aleatorio y te imprime la seed usada, para que si encuentras una falla por dependencia oculta puedas reproducir exactamente ese orden pasando la misma seed. Por otro lado, el nuevo option lock en test.describe.configure() aplica test locks a todos los tests de un archivo o grupo de una sola vez, en vez de tener que declarar el lock test por test. Esto es útil cuando varios tests paralelos tocan el mismo recurso, como la configuración de un usuario, y necesitas serializarlos sin perder el paralelismo del resto de la suite.
npx playwright test --shuffle# Running 42 tests using 4 workers, shuffle seed 271828182# ...# Pass the seed to reproduce the same order.npx playwright test --shuffle 271828182Locator.within(): combinar locators en orden natural
Cuando necesitabas un elemento dentro de otro, la forma habitual era escribir primero el contenedor y encadenar el hijo, por ejemplo dialog.getByRole('button', ...), lo que invierte el orden en que normalmente describes el elemento en lenguaje natural: "este botón, dentro de ese diálogo". locator.within() te deja escribirlo en ese orden: tomas el locator del botón que ya tenías y le aplicas within(dialog). La ganancia no es solo estilística: locators relativos como nth() o first() ahora se resuelven dentro de cada padre por separado, lo que hace triviales las consultas por columna. Por ejemplo, puedes pedir la tercera celda de cada fila de una tabla, en vez de la tercera celda del documento completo, algo que antes requería lógica adicional o selectores más específicos.
const saveButton = page.getByRole('button', { name: 'Save' });const dialog = page.getByTestId('settings-dialog');await saveButton.within(dialog).click();// The third cell of every row, not the third cell in the table.const thirdColumn = page.getByRole('cell').nth(2).within(page.getByRole('row'));await expect(thirdColumn).toHaveText(['Apple', 'Banana', 'Cherry']);APIs nuevas: refs, shadow DOM, credenciales y cookies de API
El release trae varias adiciones puntuales que conviene conocer aunque no cambien tu flujo principal. page.getByRef() localiza un elemento por su aria ref (por ejemplo e2), el identificador que reporta page.ariaSnapshot() en modo 'ai', útil si trabajas con snapshots de accesibilidad orientados a agentes. La opción includeShadow en page.content() y frame.content() serializa shadow roots abiertos como declarative shadow DOM, lo que te permite capturar el HTML completo de componentes con shadow DOM. Para WebAuthn, la nueva opción signCount fija el contador inicial de una credencial virtual, y tanto create() como get() ahora devuelven ese contador, que además se guarda y restaura junto con las credenciales en el storage state. fullConfig.filteredProjects expone en global setup y reporters la lista de proyectos que efectivamente van a correr después de aplicar el filtro --project. Y apiRequestContext suma addCookies(), cookies() y clearCookies(), reflejando los métodos equivalentes de BrowserContext pero para contextos de solo API, así que puedes manejar cookies sin necesidad de abrir un browser.
await request.addCookies([{ name: 'session-id', value: '42', url: 'https://example.com' }]);console.log(await request.cookies('https://example.com'));await request.clearCookies({ name: 'session-id' });Cambios que rompen compatibilidad
Cuatro cambios de esta versión pueden afectar tests existentes sin que cambies nada en tu código. Primero, la propiedad screen de los device descriptors ahora se propaga de verdad: si usas ...devices['Desktop Chrome'], window.screen y las media queries pasan a ver el tamaño de pantalla emulado, algo que antes se ignoraba; si dependías de ese comportamiento previo, tienes que poner screen: undefined explícitamente. Segundo, el JSX dentro de archivos de test ahora respeta tu tsconfig.json (jsx, jsxFactory, jsxFragmentFactory, jsxImportSource) en vez de un comportamiento fijo, y por defecto usa el runtime automático de react/jsx-runtime. Tercero, --update-snapshots=missing ahora hace pasar el test: antes, correr en modo 'missing' generaba el snapshot faltante pero igual marcaba el test como fallido; ahora puedes usar ese modo en CI para generar snapshots nuevos y verificar los existentes en una sola corrida, mientras que el modo 'default' mantiene el comportamiento anterior de fallar cuando falta un snapshot. Cuarto, los elementos dentro de un iframe oculto (por ejemplo con visibility: hidden) ahora se consideran ocultos para acciones, locator.isVisible() y toBeVisible(), lo que puede cambiar el resultado de aserciones que antes pasaban por error.
// playwright.config.tsexport default defineConfig({ use: { ...devices['Desktop Chrome'], screen: undefined, },});What you get
page.webmcp y frame.webmcp te permiten testear directamente las herramientas que una página registra vía WebMCP, sin montar un cliente MCP aparte.
Los videos ahora aceptan fps configurable, estilos CSS para cursor y highlights, y se codifican en VP9: útil si usas video para debugging o documentación.
testProject.default, --shuffle y lock en describe.configure() dan más control sobre qué corre en local versus CI y ayudan a detectar dependencias ocultas entre tests.
locator.within() simplifica consultas de elementos anidados y hace triviales las búsquedas por columna en tablas gracias a que nth()/first() se resuelven por padre.
Revisa los cuatro breaking changes antes de actualizar: screen emulado por defecto, JSX según tsconfig, update-snapshots=missing que ahora pasa, e iframes ocultos que ocultan su contenido.
Want a class on this?
If enough people ask for it, it goes on the schedule. No email, no name needed.