Complete Claude Code plugin with: - 9 skills (/pw:init, generate, review, fix, migrate, coverage, testrail, browserstack, report) - 3 specialized agents (test-architect, test-debugger, migration-planner) - 55 test case templates across 11 categories (auth, CRUD, checkout, search, forms, dashboard, settings, onboarding, notifications, API, accessibility) - TestRail MCP server (TypeScript) — 8 tools for bidirectional sync - BrowserStack MCP server (TypeScript) — 7 tools for cross-browser testing - Smart hooks (auto-validate tests, auto-detect Playwright projects) - 6 curated reference docs (golden rules, locators, assertions, fixtures, pitfalls, flaky tests) - Leverages Claude Code built-ins (/batch, /debug, Explore subagent) - Zero-config for core features; TestRail/BrowserStack via env vars - Both TypeScript and JavaScript support throughout Co-authored-by: Leo <leo@openclaw.ai>
4.4 KiB
4.4 KiB
Selenium → Playwright Mapping
Driver Setup
| Selenium (JS) | Playwright |
|---|---|
new Builder().forBrowser('chrome').build() |
Handled by config — no driver setup |
driver.quit() |
Automatic — Playwright manages browser lifecycle |
driver.manage().setTimeouts(...) |
Config: timeout, expect.timeout |
Navigation
| Selenium | Playwright | Notes |
|---|---|---|
driver.get(url) |
await page.goto(url) |
Use baseURL |
driver.navigate().back() |
await page.goBack() |
|
driver.navigate().forward() |
await page.goForward() |
|
driver.navigate().refresh() |
await page.reload() |
|
driver.getCurrentUrl() |
page.url() |
|
driver.getTitle() |
await page.title() |
Element Location
| Selenium | Playwright | Preferred |
|---|---|---|
By.id('x') |
page.locator('#x') |
page.getByTestId('x') |
By.css('.x') |
page.locator('.x') |
page.getByRole(...) |
By.xpath('//div') |
page.locator('xpath=//div') |
Avoid — use role-based |
By.name('x') |
page.locator('[name=x]') |
page.getByLabel(...) |
By.linkText('x') |
page.getByRole('link', { name: 'x' }) |
✅ Best practice |
By.partialLinkText('x') |
page.getByRole('link', { name: /x/ }) |
✅ Best practice |
By.tagName('button') |
page.getByRole('button') |
✅ Best practice |
By.className('x') |
page.locator('.x') |
page.getByRole(...) |
findElement() |
Returns first match | locator.first() |
findElements() |
page.locator(selector) |
Use .count() or .all() |
Actions
| Selenium | Playwright |
|---|---|
element.click() |
await locator.click() |
element.sendKeys('text') |
await locator.fill('text') |
element.sendKeys(Key.ENTER) |
await locator.press('Enter') |
element.clear() |
await locator.clear() |
element.submit() |
await locator.press('Enter') or click submit button |
element.getText() |
await locator.textContent() |
element.getAttribute('x') |
await locator.getAttribute('x') |
element.isDisplayed() |
await locator.isVisible() |
element.isEnabled() |
await locator.isEnabled() |
element.isSelected() |
await locator.isChecked() |
Waits
| Selenium | Playwright | Notes |
|---|---|---|
WebDriverWait(driver, 10).until(EC.visibilityOf(el)) |
await expect(locator).toBeVisible() |
Auto-retries |
WebDriverWait(driver, 10).until(EC.elementToBeClickable(el)) |
await locator.click() |
Auto-waits for clickable |
WebDriverWait(driver, 10).until(EC.presenceOf(el)) |
await expect(locator).toBeAttached() |
|
WebDriverWait(driver, 10).until(EC.textToBe(el, 'x')) |
await expect(locator).toHaveText('x') |
|
Thread.sleep(3000) |
❌ Never use | Use assertions instead |
driver.manage().setTimeouts({ implicit: 10000 }) |
Not needed | Playwright auto-waits |
Advanced
| Selenium | Playwright |
|---|---|
Actions(driver).moveToElement(el).perform() |
await locator.hover() |
Actions(driver).dragAndDrop(src, tgt).perform() |
await src.dragTo(tgt) |
Actions(driver).doubleClick(el).perform() |
await locator.dblclick() |
Actions(driver).contextClick(el).perform() |
await locator.click({ button: 'right' }) |
driver.switchTo().frame(el) |
page.frameLocator('#frame') |
driver.switchTo().defaultContent() |
Not needed — use page directly |
driver.switchTo().alert() |
page.on('dialog', d => d.accept()) |
driver.switchTo().window(handle) |
const popup = await page.waitForEvent('popup') |
driver.executeScript(js) |
await page.evaluate(js) |
driver.takeScreenshot() |
await page.screenshot({ path: 'x.png' }) |
Test Structure
| Selenium (Jest/Mocha) | Playwright |
|---|---|
describe('Suite', () => { ... }) |
test.describe('Suite', () => { ... }) |
it('should...', () => { ... }) |
test('should...', async ({ page }) => { ... }) |
beforeAll(() => { ... }) |
test.beforeAll(async () => { ... }) |
beforeEach(() => { ... }) |
test.beforeEach(async ({ page }) => { ... }) |
afterEach(() => { ... }) |
test.afterEach(async ({ page }) => { ... }) |
Key Differences
- No implicit waits — Playwright auto-waits for actionability
- No driver management — Playwright handles browser lifecycle
- Built-in assertions —
expect(locator)with auto-retry - Parallel by default — tests run in parallel, must be isolated
- Traces instead of screenshots — richer debugging artifacts