Cuando recibimos una solicitud para migrar pruebas de Cypress a Playwright, esperábamos un proceso de reescritura largo y tedioso.
Sorprendentemente, con la ayuda de GitHub Copilot, logramos iniciar la migración de Cypress a Playwright en solo un par de días, y mantuvimos nuestra canalización de CI funcionando todo el tiempo sin interrumpir ningún proceso de prueba.
En este artículo, compartiré cómo abordamos esta migración, las lecciones que aprendimos en el camino y cómo la migración de código asistida por IA puede reducir drásticamente la sobrecarga manual de las transiciones de frameworks.
Contexto del Proyecto
El proyecto era una compleja plataforma de atención médica con muchas cosas funcionando por debajo. Nuestra configuración de pruebas incluía:
- Pruebas de extremo a extremo (E2E) y de integración
- Una arquitectura TAF personalizada con configuraciones, desmontajes y creación de entidades
- Configuraciones de múltiples entornos
- Datos dinámicos y lógica de navegación profunda
En total, teníamos 148 pruebas E2E ejecutándose en Cypress, todas funcionando bien, pero la organización quería estandarizar las herramientas de QA en todos los equipos, lo que significaba una cosa: migración a Playwright.
¿Por qué usar GitHub Copilot para la migración?
Cuando llegó la solicitud de migración, vi una oportunidad para experimentar con la automatización de pruebas de GitHub Copilot. En lugar de reescribir manualmente cientos de archivos de prueba, quería ver hasta dónde podíamos impulsar la asistencia de la IA para automatizar la conversión de sintaxis de Cypress a Playwright.
Y, sinceramente, funcionó mucho mejor de lo esperado.
GitHub Copilot se encargó de la mayor parte de la traducción repetitiva (selectores, comandos, manejo asíncrono, etc.), lo que nos permitió centrarnos en los ajustes de infraestructura y la puesta a punto.
En 1 o 2 días, ya teníamos un prototipo funcional del framework de automatización de pruebas de Playwright.
Dos descargos de responsabilidad clave
Siempre verifica el código generado por IA
Aunque la migración de código asistida por IA acelera las cosas, debes revisar cada línea. Las migraciones de infraestructura pueden introducir fácilmente errores sutiles o regresiones de rendimiento. Piensa en Copilot como tu compañero de programación, no como un piloto automático.
Mantén el framework antiguo funcionando hasta el final
Nunca bloquees lanzamientos o regresiones durante la migración. Mantén tus pruebas de Cypress ejecutándose en CI hasta que las suites correspondientes estén completamente estables en Playwright. Una vez que una suite se migra y valida, elimínala de Cypress y cambia CI para ejecutarla en Playwright.
Este despliegue gradual garantiza cero tiempo de inactividad y una cobertura de pruebas continua durante todo el proceso.
Flujo de trabajo para migrar de Cypress a Playwright
Una vez que los cimientos estuvieron claros, pasamos a la migración práctica.
A continuación se muestra el flujo de trabajo que nos ayudó a ejecutar ambos frameworks en paralelo, mantener la estabilidad de CI y comenzar a migrar pruebas de forma incremental, sin bloquear los lanzamientos en curso.
1. Inicializa Playwright y crea la estructura de carpetas
El primer paso fue inicializar Playwright y crear una estructura de carpetas limpia y aislada. Queríamos que ambos frameworks coexistieran en el mismo repositorio, para poder ejecutarlos simultáneamente durante la transición.
Esa configuración nos permitió:
- Migrar gradualmente
- Comparar resultados y tiempos entre Cypress y Playwright
- Mantener las canalizaciones de CI funcionando para ambos
Ejemplo de inicialización:

Aquí está la estructura de carpetas propuesta que utilizamos para Playwright:
├── cypress/
│ ├── app/
│ ├── downloads/
│ ├── e2e/
│ ├── fixtures/
│ ├── screenshots/
│ ├── support/
│ └── videos/
├── playwright/
│ ├── app/
│ │ ├── components/
│ │ ├── fixtures/
│ │ └── pageobjects/
│ ├── constants/
│ ├── helpers/
│ ├── reports/
│ ├── test-results/
│ └── tests/
├── .gitignore
├── package.json
├── playwright.config.ts
├── smoke.config.ts
├── tsconfig.json2. Configura archivos tsconfig separados para cada framework
Uno de los primeros problemas que enfrentamos fueron los conflictos de TypeScript: ambos frameworks definen nombres de métodos similares (expect, request, etc.), y compartir un solo tsconfig.json llevó a errores de compilación.
La solución fue simple: dividir las configuraciones de TypeScript. Esta estructura asegura que cada framework inicialice sus propias definiciones de tipo y evita colisiones de tipos:
├── tsconfig.json
├── tsconfig.cypress.json
├── tsconfig.playwright.jsonMain tsconfig.json
Agrega la opción composite para admitir referencias de proyectos.
tsconfig.json
{
"compilerOptions": {
"composite": true
}
}tsconfig.cypress.json
{
"extends": "./tsconfig.json",
"compilerOptions": {
"types": ["cypress", "@testing-library/cypress", "node"],
"isolatedModules": false
},
"include": ["cypress/**/*.ts"]
}tsconfig.playwright.json
{
"extends": "./tsconfig.json",
"compilerOptions": {
"types": ["node", "@playwright/test"]
},
"include": ["playwright/**/*.ts", "playwright.config.ts"]
}Con esta separación, ambos frameworks pudieron coexistir pacíficamente en un solo repositorio, sin más conflictos de compilador y sin riesgo de inicializar accidentalmente métodos compartidos dos veces.
3. Crea un prompt para automatizar la migración con GitHub Copilot
Una vez que Playwright se inicializó y el proyecto se estructuró, fue hora de dejar que GitHub Copilot ayudara con el trabajo pesado.
Para ello, creamos un prompt personalizado que define cómo debe comportarse Copilot al reescribir pruebas de Cypress a Playwright. El prompt le dice al agente qué hacer, cómo hacerlo y qué ignorar, lo que brinda resultados consistentes en todas las migraciones.
Cómo añadir el prompt
Dentro de tu repositorio de GitHub, abre el ícono de engranaje ⚙️ en Copilot Chat y crea un nuevo prompt.
El archivo se guardará automáticamente en: ./github/prompts/
Puedes nombrarlo algo como migrate_tests.md.
Ejemplo de prompt:
---
mode: agent
---
You are a Playwright Test Generator, an expert in browser automation and end-to-end testing.
Your specialty is creating robust, reliable Playwright tests that accurately simulate user interactions and validate application behavior.
Your task is to help migrate existing Cypress tests to Playwright tests.
The Cypress tests, page object, components, and helper files are provided as input, and you need to generate equivalent Playwright test code.
Output ALL migrated code to the chat.
When generating the Playwright test code or any page objects, ensure that:
1. The test structure follows the same structure as the Cypress tests.
2. Create a global fixture for setup and teardown and reuse it in the tests.
3. All user interactions (clicks, typing, navigation) are accurately translated to Playwright syntax in page objects and test files.
4. Assertions in Cypress are converted to equivalent Playwright assertions.
5. Ignore the network spying and stubbing parts of the Cypress tests.
6. Comment out all the methods with waits (e.g., cy.wait) in the Playwright code.
7. Do not use get methods in page objects; use direct methods instead `backbackItem = () => this.page.locator('[id="item_4_title_link"]');`Por qué esto ayuda
Este tipo de prompt estructurado permite que GitHub Copilot se comporte más como un asistente de migración especializado, no como un generador de código de propósito general.
Con el contexto y las reglas adecuados, Copilot puede automáticamente:
- Reescribir grandes bloques de pruebas de Cypress a la sintaxis de Playwright
- Mantener la consistencia de la estructura de carpetas y pruebas
- Reducir el tiempo de conversión manual entre un 70 y un 80 %
Esencialmente, estás creando tu propia cadena de herramientas de migración de IA, adaptada a la estructura y las convenciones de tu proyecto.
4. Selecciona el modo Agente y ejecuta la migración paso a paso
Una vez que el prompt estuvo listo, el siguiente paso fue ejecutar la migración con GitHub Copilot en modo agente. En esta fase, dejamos que el modelo de IA procesara cada archivo (objetos de página, componentes, ayudantes y, finalmente, las pruebas) uno por uno.
Para esta tarea, utilizamos el modelo GPT-4.1, que ofrece las transformaciones de código más consistentes y conscientes del contexto.
Cómo iniciar la migración
1. Abre Copilot Chat en VS Code
2. Usa / para seleccionar el archivo de prompt que creaste anteriormente
3. Usa # para seleccionar un archivo específico (por ejemplo, una prueba de Cypress o un objeto de página) que desees migrar
Copilot aplicará tu prompt de migración y generará una versión del archivo compatible con Playwright directamente en el chat.
Ejemplo de migración del objeto de página inventoryPage
import { ItemComponent } from "../components/item.component";
import Common from "./common";
class InventoryPage extends Common {
item = new ItemComponent();
backbackItem = () => cy.get("#item_4_title_link");
backbackAddItemButton = () => cy.getByTestId("add-to-cart-sauce-labs-backpack");
bikeLightsAddItemButton = () => cy.getByTestId("add-to-cart-sauce-labs-bike-light");
backbackRemoveItemButton = () => cy.getByTestId("remove-sauce-labs-backpack");
shoppingCartLogo = () => cy.get("#shopping_cart_container");
clickBackbackAddItemButton = () => this.backbackAddItemButton().click();
clickBikeLightsAddItemButton = () => this.bikeLightsAddItemButton().click();
clickBackbackRemoveItemButton = () => this.backbackRemoveItemButton().click();
assertCartLogoItems = (itemsNum: number) => this.shoppingCartLogo().should("have.text", itemsNum);
}
export default new InventoryPage();
import { Page, Locator, expect } from "@playwright/test";
import Common from "./common";
class InventoryPage extends Common {
constructor(page: Page) {
super(page);
this.item = new ItemComponent(page);
}
// Locators
backbackItem(): Locator {
return this.page.locator("#item_4_title_link");
}
backbackAddItemButton(): Locator {
return this.page.getByTestId("add-to-cart-sauce-labs-backpack");
}
bikeLightsAddItemButton(): Locator {
return this.page.getByTestId("add-to-cart-sauce-labs-bike-light");
}
backbackRemoveItemButton(): Locator {
return this.page.getByTestId("remove-sauce-labs-backpack");
}
shoppingCartLogo(): Locator {
return this.page.locator("#shopping_cart_container");
}
// Actions
async clickBackbackAddItemButton(): Promise<void> {
await this.backbackAddItemButton().click();
}
async clickBikeLightsAddItemButton(): Promise<void> {
await this.bikeLightsAddItemButton().click();
}
async clickBackbackRemoveItemButton(): Promise<void> {
await this.backbackRemoveItemButton().click();
}
async assertCartLogoItems(itemsNum: number): Promise<void> {
await expect(this.shoppingCartLogo()).toHaveText(String(itemsNum));
}
}
export default InventoryPage;Ajustando el comportamiento del Agente
No te preocupes si la primera salida no es perfecta. Según nuestra experiencia, el agente de IA suele acertar entre el 60 y el 70 % del código en el primer intento. La clave es mantener activo el contexto de la conversación: continúa refinando el mismo hilo de chat pidiendo correcciones o ajustes. Al hacerlo, Copilot comienza a aprender los patrones de tu proyecto y produce código más preciso y reutilizable.
Esencialmente, cuantas más iteraciones hagas en el mismo chat, mejor será la calidad de la migración.
Orden de migración: Archivos primero, luego pruebas
Para mantener la coherencia de las dependencias y evitar referencias perdidas, sigue este orden:
1. Genera primero los archivos no relacionados con las pruebas:
- Objetos de página
- Componentes
- Archivos de ayuda
- Constantes
2. Una vez que todos los archivos de soporte estén generados y verificados, agrega la carpeta #app al contexto del chat y luego migra los propios archivos de prueba.
Paso de verificación
Cuando se genera una prueba de Playwright:
1. Revisa el código: confirma que los localizadores de página, los pasos de prueba y las aserciones sean correctos.
2. Ejecuta la prueba usando tu CLI de Playwright para asegurarte de que se ejecuta correctamente.
3. Corrige pequeños errores de sintaxis o de coincidencia de fixtures si Copilot malinterpretó un comando de Cypress.
Al verificar después de cada archivo, mantienes la migración estable e incremental, evitando la depuración a gran escala más adelante.
Ejemplo de prueba de inventoryPage migrada que pasa con éxito:
import inventoryPage from "cypress/app/pageobjects/inventoryPage";
import loginPage from "cypress/app/pageobjects/loginPage";
import { itemsNames } from "../../fixtures/data.json";
describe("InventoryPage tests", () => {
beforeEach(() => {
loginPage.loginWithValidData();
});
it("The user should add item to the card", () => {
inventoryPage.item.itemByName("Bike Light").should("have.text", itemsNames.bikeLight);
inventoryPage.item.addToCartByName("Bike Light");
inventoryPage.assertCartLogoItems(1);
});
it("The user should remove item from the card", () => {
inventoryPage.backbackItem().should("have.text", itemsNames.backpackItemName);
inventoryPage.clickBackbackAddItemButton();
inventoryPage.assertCartLogoItems(1);
inventoryPage.clickBackbackRemoveItemButton();
inventoryPage.shoppingCartLogo().should("not.have.text");
});
it("The user should add multiple items to the card", () => {
inventoryPage.backbackItem().should("have.text", itemsNames.backpackItemName);
inventoryPage.clickBackbackAddItemButton();
inventoryPage.assertCartLogoItems(1);
inventoryPage.clickBikeLightsAddItemButton();
inventoryPage.assertCartLogoItems(2);
});
});
import { test, expect } from "../fixtures/test.fixture";
import { itemsNames } from "../../constants/data.json";
// InventoryPage tests migrated from Cypress to Playwright
test.describe("InventoryPage tests", () => {
test.beforeEach(async ({ pageManager }) => {
await pageManager.loginPage.loginWithValidData();
});
test("The user should add item to the cart", async ({ pageManager }) => {
await expect(
pageManager.inventoryPage.item.itemByName(itemsNames.bikeLight)).toHaveText(itemsNames.bikeLight);
await pageManager.inventoryPage.item.addToCartByName(itemsNames.bikeLight);
await pageManager.inventoryPage.assertCartLogoItems(1);
});
test("The user should remove item from the cart", async ({ pageManager }) => {
await expect(
pageManager.inventoryPage.backbackItem()).toHaveText(itemsNames.backpackItemName);
await pageManager.inventoryPage.clickBackbackAddItemButton();
await pageManager.inventoryPage.assertCartLogoItems(1);
await pageManager.inventoryPage.clickBackbackRemoveItemButton();
await expect(pageManager.inventoryPage.shoppingCartLogo()).not.toHaveText(/\d/); // Should not have any number text
});
test("The user should add multiple items to the cart", async ({ pageManager }) => {
await expect(pageManager.inventoryPage.backbackItem()).toHaveText(itemsNames.backpackItemName);
await pageManager.inventoryPage.clickBackbackAddItemButton();
await pageManager.inventoryPage.assertCartLogoItems(1);
await pageManager.inventoryPage.clickBikeLightsAddItemButton();
await pageManager.inventoryPage.assertCartLogoItems(2);
});
});5. Estructura final del repositorio después de la migración
Una vez completada la migración, nuestro repositorio alcanzó un estado estable de doble framework, ejecutando completamente Cypress y Playwright lado a lado. Esta estructura nos permitió:
- Depreciar gradualmente las suites de pruebas de Cypress a medida que se reemplazaban
- Mantener las canalizaciones de CI/CD operativas para ambos frameworks
- Mantener una arquitectura limpia y modular
A continuación se muestra la estructura final después de completar el flujo de trabajo de migración.
Estructura final de carpetas
├── .env
├── .gitignore
├── readme.md
├── package.json
├── playwright.config.ts
├── smoke.config.ts
├── tsconfig.cypress.json
├── tsconfig.playwright.json
├── tsconfig.json
├── .github/
│ ├── prompts/
│ └── workflows/
├── cypress/
│ ├── app/
│ ├── downloads/
│ ├── e2e/
│ ├── fixtures/
│ ├── screenshots/
│ ├── support/
│ └── videos/
├── playwright/
│ ├── app/
│ ├── constants/
│ ├── helpers/
│ ├── reports/
│ ├── test-results/
│ └── tests/
Notas sobre la estructura
.github/prompts/— contiene tus prompts de migración de Copilot. Mantenerlos versionados te permite actualizarlos o reutilizarlos para futuras migraciones o refactorizaciones de frameworks..github/workflows/— guarda tus definiciones de CI/CD. Puedes ejecutar trabajos de Playwright y Cypress en paralelo hasta la depreciación completa del framework antiguo.- La carpeta
playwright/— ahora actúa como la fuente de automatización principal en adelante. Refleja la estructura de la implementación anterior de Cypress para facilitar la incorporación y asegurar la consistencia. tsconfig.playwright.jsonytsconfig.cypress.json— permanecen separados para un aislamiento total de tipos, evitando conflictos durante las compilaciones.
Esta estructura no solo apoyó una transición fluida, sino que también posicionó el proyecto para una futura escalabilidad, flexibilidad de CI y propiedad modular de las pruebas en todos los equipos.
Consejos prácticos y lecciones aprendidas
Después de migrar casi 150 pruebas con Copilot y Playwright, aquí tienes algunas lecciones clave y consejos del mundo real que hicieron el proceso más fluido (y nos salvaron de algunos dolores de cabeza).
Consejos generales relacionados con la IA
- La IA miente, a menudo de forma convincente.
Siempre verifica cada línea de código que genera la IA. Nunca confíes ciegamente en ella, especialmente durante las migraciones de infraestructura.
- Apégate a convenciones de nomenclatura consistentes.
Mantén nombres de variables y métodos idénticos en todos los frameworks; ayuda al agente de IA a migrar el código con mayor precisión y te facilita encontrarlos e importarlos más tarde.

- Reconstruye las importaciones manualmente.
Copilot tiende a estropear las rutas de importación. A menudo es más rápido eliminarlas y volver a importarlas manualmente, o configurar importaciones globales como: import x from "playwright/foo/bar";
- Reutiliza el mismo chat para todas las migraciones.
Mantente en un único hilo de chat de Copilot: construye contexto y "recuerda" las convenciones de tu proyecto, lo que lleva a mejores resultados.
- Descompón las tareas de migración.
Genera 1 o 2 archivos a la vez. Las tareas más pequeñas producen resultados más precisos y facilitan la validación manual.
Consejos específicos de Playwright
- Nunca crees múltiples instancias del mismo objeto de página sin necesidad.
Hacerlo puede causar un estado inconsistente o selectores rotos al navegar entre páginas. ¿La solución? Implementa un Page Manager que mantenga todos los objetos de página accesibles a través de una única instancia.
Aquí tienes un ejemplo de configuración mínima:
// login.page.ts
class LoginPage extends Common {
async clickLoginButton(): Promise<void> {
await this.loginButton().click();
}
}
export default LoginPage;
//-----------------------------------------------------------
//PageManager.ts
export class PageManager {
readonly loginPage: LoginPage;
constructor(page: Page) {
this.loginPage = new LoginPage(page);
}
}
//-----------------------------------------------------------
//test.fixture.ts
export const test = base.extend<{
pageManager: PageManager;
}>({
pageManager: async ({ page }, use) => {
const manager = new PageManager(page);
await use(manager);
},
});
export { expect };
//-----------------------------------------------------------
//userLogin.spec.ts
test("The user should login with valid data", async ({ pageManager }) => {
await pageManager.loginPage.clickLoginButton();
});Otros consejos técnicos
- Elimina todos los prefijos "get" de los localizadores que genera el Agente de IA; usa métodos de localizador directos en su lugar.
- No olvides configurar
dotenvpara las variables de entorno y las credenciales. - Personaliza tu configuración de Playwright para que todas las pruebas se generen en la carpeta de Playwright, de modo que la raíz de tu proyecto permanezca limpia:
reporter: [["html", { open: "never", outputFolder: "./playwright/reports/" }]],
outputDir: "./playwright/test-results",
Epílogo
No incluí la configuración de ESLint aquí, ya que varía de un proyecto a otro.
La única recomendación universal que puedo hacer es instalar el plugin ESLint de Playwright para una mejor aplicación de reglas: eslint-plugin-playwright.
Si deseas explorar un ejemplo completo y funcional de esta migración, incluidos los archivos de prompt, tsconfigs y la configuración de CI, puedes consultar el repositorio completo aquí: Ejemplo de repositorio














