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.
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
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.
