A Selenium-to-Playwright locator and action comparison
The mechanical translation matters less than the design shift it enables, but seeing them side by side is where most teams start:
// Selenium (Java), explicit wait then click
WebDriverWait wait = new WebDriverWait(driver, Duration.ofSeconds(10));
WebElement checkoutButton = wait.until(
ExpectedConditions.elementToBeClickable(By.xpath("//button[@data-action='checkout']"))
);
checkoutButton.click();
// Playwright (TypeScript), auto-waiting built into the locator/action
await page.locator("//button[@data-action='checkout']").click();
The Playwright version isn't just shorter. page.locator(...).click() auto-waits for the element to be attached, visible, stable, and receiving events before it acts, which is what most of those hand-written WebDriverWait blocks in a legacy Selenium suite exist to approximate manually (Verified, Playwright documentation). This is also the point in a migration where it's worth revisiting locator choice itself, not just syntax: a legacy suite's XPath and CSS selectors carried over verbatim will still be as brittle as they were in Selenium. The companion article on locator strategy (linked below) covers how to choose resilient locators for the rewritten tests rather than porting fragile ones forward.
Running both suites in the same CI pipeline during the transition window
name: migration-parallel-run
on:
pull_request:
push:
branches: [main]
jobs:
selenium-tier1:
if: contains(github.event.pull_request.labels.*.name, 'tier1-migrating') || github.event_name == 'push'
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- run: mvn test -Dgroups=tier1
playwright-tier1:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- run: npm ci
- run: npx playwright test --grep @tier1
compare-results:
needs: [selenium-tier1, playwright-tier1]
runs-on: ubuntu-latest
steps:
- run: echo "Archive both result sets with a shared build ID for audit comparison"
The compare-results step is intentionally minimal here: the point is that both result sets get archived against the same build identifier, so a later audit or a later engineer can reconstruct exactly what each suite reported for any given release during the transition window, not just today's CI summary.
Audit trail continuity during migration
For regulated-industry teams, the artifact that matters isn't "we migrated," it's "coverage was continuous and provable throughout." Three concrete practices support that:
- Tag each test with its control or requirement ID in both frameworks, not just a tier label, so a compliance mapping (test to control) survives the framework change instead of needing to be rebuilt from scratch afterward.
- Archive parallel-run results with retention matching your audit window, not your CI's default log retention, which is often 90 days or less and won't cover a migration that spans a full audit cycle.
- Keep a written migration log per tier: date range of the parallel run, discrepancies found and how they were resolved, and the date Selenium coverage for that tier was formally retired. This is the document that answers an auditor's question directly instead of requiring them to reconstruct history from CI logs.