JetBase Logotyp
  • Hem
  • Blogg
  • Migrera Cypress-tester till Playwright med Copilot
Banner

Viktigaste punkterna

En migration från Cypress till Playwright behöver inte bli en högriskomskrivning. På en vårdplattform flyttades 148 E2E-tester gradvis medan Cypress förblev aktiv i CI, vilket bevarade release-täckningen under hela övergången. GitHub Copilot minskade repetitivt konverteringsarbete med 70–80%, men säker migration krävde fortfarande mänsklig granskning, isolerade ramverkskonfigurationer och fil för fil-validering.

• Bygg Playwright-prototypen på 1–2 dagar.
• Kör båda ramverken tills varje svit är stabil.
• Migrera stödjande filer före testfiler.
• Granska importer, fixtures, locatorer och påståenden manuellt.

När vi fick en begäran att migrera Cypress-tester till Playwright, förväntade vi oss en lång och tröttsam omskrivningsprocess.

Överraskande nog lyckades vi, med hjälp av GitHub Copilot, starta migreringen från Cypress till Playwright på bara ett par dagar — och hålla vår CI-pipeline igång hela tiden utan att avbryta några testprocesser.

I den här artikeln kommer jag att dela med mig av hur vi angrep denna migrering, de lärdomar vi drog längs vägen och hur AI-assisterad kodmigrering drastiskt kan minska den manuella belastningen vid ramövergångar.

1

Projektkontext

Projektet var en komplex hälso- och sjukvårdsplattform med mycket som pågick under ytan. Vår testkonfiguration inkluderade:

  • End-to-end (E2E) och integrationstester
  • En anpassad TAF-arkitektur med inställningar, nedmonteringar och skapande av enheter
  • Multi-miljökonfigurationer
  • Dynamiska data och djup navigationslogik

Totalt hade vi 148 E2E-tester som kördes på Cypress — allt fungerade bra, men organisationen ville standardisera QA-verktyg över team, vilket innebar en sak: Playwright-migrering.

2

Varför använda GitHub Copilot för migrering?

När migrationsbegäran kom såg jag en möjlighet att experimentera med GitHub Copilot för testautomatisering. Istället för att manuellt skriva om hundratals testfiler ville jag se hur långt vi kunde driva AI-assistans för att automatisera syntaxkonverteringen från Cypress till Playwright.

Och ärligt talat fungerade det mycket bättre än förväntat.

GitHub Copilot hanterade det mesta av den repetitiva översättningen (väljare, kommandon, asynkron hantering, etc.), vilket lät oss fokusera på justeringar av infrastrukturen och finjustering.

Inom 1–2 dagar hade vi redan en fungerande prototyp av Playwright-testautomatiseringsramverket.

3

Två viktiga förbehåll

Verifiera alltid AI-genererad kod

Även om AI-assisterad kodmigrering påskyndar saker och ting, måste du granska varje rad. Infrastruktursmigreringar kan lätt introducera subtila buggar eller prestandaförsämringar. Tänk på Copilot som din parprogrammerare, inte som en autopilot.

Håll det gamla ramverket igång till slutet

Blockera aldrig releaser eller regressioner under migreringen. Håll dina Cypress-tester igång på CI tills motsvarande sviter är helt stabila i Playwright. När en svit har migrerats och validerats, ta bort den från Cypress och byt CI för att köra den i Playwright.

Denna gradvisa utrullning säkerställer noll driftstopp och kontinuerlig testtäckning under hela processen.

4

Arbetsflöde för att migrera från Cypress till Playwright

När grunden var klar gick vi vidare till den praktiska migreringen.  
Här nedan är arbetsflödet som hjälpte oss att köra båda ramverken parallellt, upprätthålla CI-stabilitet och börja migrera tester stegvis — utan att blockera pågående releaser.

1. Initiera Playwright och skapa mappstrukturen

Det första steget var att initiera Playwright och skapa en ren, isolerad mappstruktur. Vi ville att båda ramverken skulle leva sida vid sida i samma arkiv, så att vi kunde utföra dem samtidigt under övergången.

Den här uppsättningen gjorde att vi kunde:

  • Migrera gradvis
  • Jämföra resultat och tider mellan Cypress och Playwright
  • Hålla CI-pipelines igång för båda

Exempel på initiering:

Initialization example.webp

Här är den föreslagna mappstrukturen vi använde för 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.json

2. Konfigurera separata tsconfig-filer för varje ramverk

En av de tidiga problemen vi stod inför var TypeScript-konflikter — båda ramverken definierar liknande metodnamn (expect, request, osv.), och att dela en enda tsconfig.json ledde till kompilationsfel.

Lösningen var enkel: dela upp TypeScript-konfigurationerna. Denna struktur säkerställer att varje ramverk initierar sina egna typdefinitioner och undviker typkollisioner:

├── tsconfig.json
├── tsconfig.cypress.json
├── tsconfig.playwright.json

Huvud tsconfig.json
Lägg till composite-alternativet för att stödja projektreferenser.

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"]
}

Med denna uppdelning på plats kunde båda ramverken samexistera fredligt i ett arkiv — inga fler kompilator-konflikter och ingen risk för att av misstag initiera delade metoder två gånger.

3. Skapa en prompt för att automatisera migrering med GitHub Copilot

När Playwright hade initierats och projektet strukturerats var det dags att låta GitHub Copilot hjälpa till med det tunga arbetet.

För att göra detta skapade vi en anpassad prompt som definierar hur Copilot ska bete sig när den skriver om tester från Cypress till Playwright. Prompten talar om för agenten vad den ska göra, hur den ska göra det och vad den ska ignorera — vilket ger konsekventa resultat över alla migreringar.

Hur man lägger till prompten

Inuti ditt GitHub-repo, öppna kugghjulsikonen ⚙️ i Copilot Chat och skapa en ny prompt.  
Filens namn kommer automatiskt att sparas under: ./github/prompts/

Du kan namnge den något i stil med migrate_tests.md.

Exempel på prompt:

---
mode: agent
---
Du är en Playwright Test Generator, en expert på webbläsarautomatisering och end-to-end-testning.
Din specialitet är att skapa robusta, tillförlitliga Playwright-tester som exakt simulerar användarinteraktioner och validerar applikationsbeteende.
Din uppgift är att hjälpa till att migrera befintliga Cypress-tester till Playwright-tester.
Cypress-tester, sidobjekt, komponenter och hjälpfiler ges som indata, och du behöver generera motsvarande Playwright-testkod.
Skriv ut ALL migrerad kod till chatten.
När du genererar Playwright-testkod eller några sidobjekt, se till att:
1. Teststrukturen följer samma struktur som Cypress-testerna.
2. Skapa en global fixture för setup och teardown och återanvänd den i testerna.
3. Alla användarinteraktioner (klick, skrivning, navigering) översätts noggrant till Playwright-syntax i sidobjekt och testfiler.
4. Påståenden i Cypress omvandlas till motsvarande Playwright-påståenden.
5. Ignorera nätverksövervakningen och stubbningen av Cypress-tester.
6. Kommentera ut alla metoder med väntetider (t.ex., cy.wait) i Playwright-koden.
7. Använd inte get-metoder i sidobjekt; använd direkta metoder istället `backbackItem = () => this.page.locator('[id="item_4_title_link"]');`

Varför detta hjälper

Denna typ av strukturerad prompt gör att GitHub Copilot beter sig mer som en specialiserad migrationsassistent, snarare än en allmänt använd kodgenerator.

Med rätt sammanhang och regler kan Copilot automatiskt:

  • Omskriva stora delar av Cypress-tester till Playwright-syntax
  • Behålla konsekvens i mapp- och teststruktur 
  • Minimera manuell konverteringstid med 70–80%

I grund och botten skapar du din egen AI-migreringsverktygslåda — skräddarsydd för din projekts struktur och konventioner.

4. Välj agentläge och kör migreringen steg för steg

När prompten var klar var nästa steg att faktiskt köra migreringen med GitHub Copilot i agentläge. I detta skede lät vi AI-modellen bearbeta varje fil — sidobjekt, komponenter, hjälpare och slutligen tester — en och en.

För den här uppgiften använde vi GPT-4.1-modellen, som levererar de mest konsekventa och kontextmedvetna kodtransformeringarna.

Hur man börjar migrera

1. Öppna Copilot Chat i VS Code

2. Använd / för att välja den promptfil du skapade tidigare

3. Använd # för att välja en specifik fil (till exempel ett Cypress-test eller en sidaobjekt) som du vill migrera

Copilot kommer nu att tillämpa din migrationsprompt och generera en Playwright-kompatibel version av filen direkt i chatten.

How to Start Migration.webpExempel på migration av inventoryPage sidobjekt

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;

Justera agentens beteende

Oroa dig inte om det första resultatet inte är perfekt. Vår erfarenhet är att AI-agenten vanligtvis får 60–70 % av koden rätt vid första försöket. Nyckeln är att hålla samtalskontexten aktiv — fortsätt förfina resultatet i samma chattråd genom att be om korrigeringar eller justeringar. Efter hand börjar Copilot lära sig mönstren i ditt projekt och producerar mer exakt, återanvändbar kod.

Kort sagt: ju fler iterationer du gör i samma chatt, desto bättre blir migreringens kvalitet.

Migreringsordning: filer först, sedan tester

Följ den här ordningen för att hålla beroenden konsekventa och undvika saknade referenser:

1. Generera först de filer som inte är tester:

  • Sidobjekt
  • Komponenter
  • Hjälpfiler
  • Konstanter

2. När alla stödfiler är genererade och verifierade lägger du till mappen #app i chattkontexten och migrerar sedan själva testfilerna.

Verifieringssteg

När ett Playwright-test har genererats:

1. Granska koden — kontrollera att sidlokatorer, teststeg och assertions är korrekta.

2. Kör testet med ditt Playwright CLI för att säkerställa att det fungerar som det ska.

3. Åtgärda mindre avvikelser i syntax eller fixtures om Copilot har missförstått ett Cypress-kommando.

Genom att verifiera efter varje fil håller du migreringen stabil och stegvis och slipper omfattande felsökning senare.

Exempel på ett migrerat inventoryPage-test som går igenom:

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. Slutlig repositorystruktur efter migreringen

När migreringen var klar nådde vårt repository ett stabilt läge med två ramverk — Cypress och Playwright körde fullt ut sida vid sida. Den här strukturen gjorde det möjligt för oss att:

  • Gradvis fasa ut Cypress-testsviter i takt med att de ersattes
  • Hålla CI/CD-pipelines igång för båda ramverken
  • Upprätthålla en ren och modulär arkitektur

Nedan visas den slutliga strukturen efter att migreringsarbetsflödet slutförts.

Slutlig mappstruktur

├── .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/

Kommentarer om strukturen

  • .github/prompts/ — innehåller dina Copilot-prompter för migreringen. Genom att versionshantera dem kan du uppdatera eller återanvända dem vid framtida ramverksmigreringar eller refaktoreringar.
  • .github/workflows/ — innehåller dina CI/CD-definitioner. Du kan köra Playwright- och Cypress-jobb parallellt tills det gamla ramverket är helt utfasat.
  • Mappen playwright/ — fungerar nu som den primära källan för automatisering. Den speglar strukturen i den tidigare Cypress-implementeringen för att underlätta introduktionen och säkerställa konsekvens.
  • tsconfig.playwright.json och tsconfig.cypress.json — hålls separata för fullständig typisolering och förhindrar konflikter vid byggen.

Den här strukturen gav inte bara en smidig övergång, utan förberedde också projektet för framtida skalbarhet, CI-flexibilitet och modulärt testägarskap mellan team.

 
Planerar du en migrering från Cypress till Playwright?

Flytta din testsvit utan att störa releaser eller förlora täckning. Våra QA-ingenjörer kan hjälpa dig att planera migreringen, anpassa din automatiseringsarkitektur och hålla båda ramverken stabila under övergången.

5

Praktiska råd & lärdomar

Efter att ha migrerat nästan 150 tester med Copilot och Playwright kommer här några viktiga lärdomar och praktiska tips som gjorde processen smidigare (och besparade oss en del huvudvärk).

Allmänna råd om AI

  • AI ljuger — ofta övertygande.  

Verifiera alltid varje kodrad som AI:n genererar. Lita aldrig blint på den, särskilt inte vid infrastrukturmigreringar.

  • Håll dig till konsekventa namngivningskonventioner.

Använd identiska variabel- och metodnamn i båda ramverken — det hjälper AI-agenten att migrera koden mer exakt och gör det lättare för dig att hitta och importera dem senare.

Allmänna råd om AI.webp

  • Bygg om importer manuellt.

Copilot har en tendens att röra till importsökvägar. Ofta går det snabbare att ta bort dem och importera om manuellt, eller att sätta upp globala importer som: import x from "playwright/foo/bar";

  • Återanvänd samma chatt för alla migreringar.

Håll dig till en enda Copilot-chattråd — den bygger upp kontext och "kommer ihåg" ditt projekts konventioner, vilket ger bättre resultat.

  • Dela upp migreringsuppgifterna.  

Generera 1–2 filer åt gången. Mindre uppgifter ger mer exakta resultat och gör manuell validering enklare.

Playwright-specifika råd

  • Skapa aldrig flera instanser av samma Page Object utan anledning.

Det kan leda till inkonsekvent tillstånd eller trasiga selektorer när du navigerar mellan sidor. Lösningen? Implementera en Page Manager som gör alla sidobjekt tillgängliga via en enda instans.

Här är ett minimalt exempel på upplägg:

// 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();
  });

Andra tekniska tips

  • Ta bort alla "get"-prefix från lokatorerna som AI-agenten genererar — använd direkta lokatormetoder i stället.
  • Glöm inte att konfigurera dotenv för miljövariabler och inloggningsuppgifter.
  • Anpassa din Playwright-konfiguration så att alla rapporter hamnar i Playwright-mappen och projektroten hålls ren:
reporter: [["html", { open: "never", outputFolder: ".‌/playwright/reports/" }]],
  outputDir: ".‌/playwright/test-results",
6

Efterord

Jag tog inte med ESLint-konfigurationen här eftersom den varierar från projekt till projekt.  
Den enda universella rekommendationen jag kan ge är att installera Playwright-pluginet för ESLint för bättre regelefterlevnad: eslint-plugin-playwright.

Om du vill utforska ett komplett, fungerande exempel på den här migreringen — inklusive promptfiler, tsconfig-filer och CI-konfiguration — kan du se hela repositoryt här: Repo-exempel.

 
Redo att modernisera din testautomatisering?

Oavsett om du migrerar från Cypress, bygger om en instabil testsvit eller förbättrar din QA-automatisering kan JetBase hjälpa dig med övergången med mindre manuellt arbete och lägre leveransrisk.

Kommentarer

Logga in för att lämna en kommentar
Fortsätt med GoogleFortsätt med Google
Modern

Våra kundcase

Innovation handlar inte bara om idéer - det handlar om utförande, att förvandla vision till verklighet och skapa lösningar som verkligen gör intryck. Se vad vi har byggt och hur det fungerar:

  • Vård
  • Media och Underhållning
  • e-handel
  • Amazon Web Services
  • Molnkostnadsoptimering
  • Serverlös applikation
  • Detaljhandel
  • Hälsovård - Banner
    • 100%HIPAA-kompatibel
    • 99,99 % drifttidMaximal pålitlighet
  • Hälsovård - Banner
    • 10 000+Patientstödjande
    • Engagemang 30 %+Adaptiv UX

Senaste Artiklarna