Selenium to Playwright Migration Part 5: Waits, Retries, and Infrastructure
This is part of the Selenium to Playwright Migration Series. Follow the complete 7-part tutorial to migrate your test suite from Selenium Java to Playwright TypeScript.
๐ญ Want to master this with real projects? Join the Playwright Automation Mastery course at The Testing Academy.
The infrastructure layer changes dramatically: WebDriverWait becomes auto-wait, RetryAnalyzer becomes a config line, ScreenshotListener becomes a flag, and ThreadLocal driver becomes browser contexts.
Contents
Wait Strategy Conversion
Before: WaitHelpers.java (60+ lines)
public class WaitHelpers {
private WebDriverWait wait;
public WaitHelpers(WebDriver driver) {
this.wait = new WebDriverWait(driver, Duration.ofSeconds(10));
}
public void waitForVisibility(WebElement element) {
wait.until(ExpectedConditions.visibilityOf(element));
}
public void waitForClickable(WebElement element) {
wait.until(ExpectedConditions.elementToBeClickable(element));
}
public void waitForText(WebElement element, String text) {
wait.until(ExpectedConditions.textToBePresentInElement(element, text));
}
}
After: Delete the file entirely
// No WaitHelper needed in Playwright!
// Every action auto-waits for element to be:
// - Attached to DOM
// - Visible
// - Stable (no animation)
// - Enabled
// - Not obscured by other elements
await page.getByRole('button', { name: 'Submit' }).click();
// ^ Automatically waits for all 5 conditions before clicking
// For assertions, expect() auto-retries:
await expect(page.getByText('Success')).toBeVisible();
// ^ Retries every 100ms until visible or timeout (default 5s)
// Custom timeout when needed:
await expect(page.getByText('Report ready')).toBeVisible({ timeout: 30_000 });
Retry Logic: RetryAnalyzer to Config
๐ Level Up Your Playwright
From locators to CI pipelines โ build a production-grade Playwright + TypeScript framework step by step.
Before: 40+ lines of Java
public class RetryAnalyzer implements IRetryAnalyzer {
private int retryCount = 0;
private static final int MAX_RETRY = 2;
public boolean retry(ITestResult result) {
if (retryCount < MAX_RETRY) { retryCount++; return true; }
return false;
}
}
public class RetryListener implements IAnnotationTransformer {
public void transform(ITestAnnotation annotation, ...) {
annotation.setRetryAnalyzer(RetryAnalyzer.class);
}
}
After: 1 line in config
// playwright.config.ts
export default defineConfig({
retries: process.env.CI ? 2 : 0, // That's it. Done.
});
Screenshot and Video: Listeners to Config
// playwright.config.ts
use: {
screenshot: 'only-on-failure', // Replaces ScreenshotListener.java
video: 'retain-on-failure', // No Selenium equivalent
trace: 'on-first-retry', // Step-by-step replay of every action
}
Test Isolation: ThreadLocal to BrowserContext
In Selenium, you used ThreadLocal<WebDriver> for parallel test isolation. In Playwright, each test automatically gets its own BrowserContext โ isolated cookies, localStorage, and session state. Zero configuration needed.
Next: Part 6: AI-Powered Migration โ using Claude Code and LLMs to automate the conversion process.
๐ Master Playwright End to End
Join hundreds of SDETs building real automation frameworks. Lifetime access, hands-on projects, and a job-ready portfolio.
