Skip to main content
Version: 1.x

From Selenium WebDriver

Migrating from Selenium WebDriver (Java, Python, C#, or JavaScript) to Playwright + Praman for SAP Fiori and UI5 web application testing. This guide maps every core Selenium concept to its Praman equivalent, compares configuration, and provides a step-by-step migration path.

In this guide
  • Replace WebDriver protocol, driver management, and Selenium Grid with zero-infrastructure Playwright
  • Convert By.id(), By.xpath(), and By.css() selectors to UI5-aware UI5Selector objects
  • Eliminate explicit waits and Thread.sleep() with automatic waitForUI5Stable()
  • Migrate Page Object Model classes to Playwright fixtures with dependency injection
  • Adopt SAP-specific capabilities that have no Selenium equivalent (auth, FLP navigation, OData)

Quick Comparison

AspectSelenium WebDriverPraman (Playwright)
ProtocolWebDriver (W3C) over HTTPChrome DevTools Protocol (CDP) / BiDi
Selector modelBy.id, By.xpath, By.css, By.classNameUI5Selector object (controlType, properties, binding)
UI5 awarenessNone (raw DOM only)Native UI5 control registry access
Auto-waitingNone (manual WebDriverWait + ExpectedConditions)Built-in waitForUI5Stable() + auto-retry
Parallel executionSelenium Grid / TestNG / pytest-xdistNative Playwright workers
CI/CD integrationRequires Grid or cloud serviceZero infrastructure (npx playwright test)
Browser installManual driver management (ChromeDriver)npx playwright install (auto-managed)
LanguageJava, Python, C#, JavaScript, Ruby, KotlinTypeScript (first-class)
Test runnerJUnit, TestNG, pytest, NUnit, MochaPlaywright Test (built-in)
Auth managementManual login scripts6 built-in strategies + setup projects
ReportingAllure, ExtentReports (third-party)Built-in HTML, JSON, JUnit reporters
AI integrationNoneBuilt-in capabilities, recipes, agentic handler
OData helpersNoneModel-level + HTTP-level CRUD
FLP navigationManual URL construction11 typed navigation methods
Trace / debugScreenshots onlyFull trace (DOM snapshots, network, console, video)
Page Object ModelClass-based POMsPlaywright fixtures (dependency injection)

Core API Mapping

// Selenium (Java)
driver.get("https://sap-system.example.com/.../FioriLaunchpad.html#PurchaseOrder-manage");

WebElement button = driver.findElement(By.id("__xmlview0--saveBtn"));
WebElement input = driver.findElement(By.xpath("//input[@aria-label='Vendor']"));
button.click();
input.sendKeys("100001");

String text = driver.findElement(By.id("__xmlview0--statusText")).getText();
assertEquals("Active", text);
assertTrue(driver.findElement(By.id("__xmlview0--saveBtn")).isDisplayed());
// Praman
import { test, expect } from 'playwright-praman';

test('PO overview', async ({ ui5, ui5Navigation }) => {
await ui5Navigation.navigateToApp('PurchaseOrder-manage');

await ui5.click({ controlType: 'sap.m.Button', properties: { text: 'Save' } });
await ui5.fill({ id: 'vendorInput' }, '100001');

const status = await ui5.control({ id: 'statusText' });
await expect(status).toHaveUI5Text('Active');
await expect(await ui5.control({ id: 'saveBtn' })).toBeUI5Visible();
});

Waiting for Elements

// Selenium (Java) -- manual explicit waits
WebDriverWait wait = new WebDriverWait(driver, Duration.ofSeconds(30));
wait.until(ExpectedConditions.visibilityOfElementLocated(By.id("__xmlview0--poTable")));
Thread.sleep(5000); // common anti-pattern
// Praman -- no explicit waits needed
// ui5.control() automatically waits for UI5 bootstrap, pending OData requests,
// JS timeouts/intervals, and DOM stabilization before returning
const table = await ui5.control({ controlType: 'sap.m.Table', id: 'poTable' });
await expect(table).toHaveUI5RowCount({ min: 1 });
Common mistake

Do not carry over Selenium's implicitlyWait pattern by adding page.waitForTimeout() calls in Praman tests. Praman's ui5.control() automatically waits for the UI5 framework to stabilize (pending OData requests, JS timeouts, DOM settle) before returning. Adding manual waits defeats this mechanism and slows your tests.

Selector Mapping

Selenium selectors target raw DOM elements. Praman selectors target the UI5 control registry, which survives UI5 version upgrades, theme changes, and DOM restructuring.

Selenium SelectorPraman UI5Selector EquivalentNotes
By.id("__xmlview0--myBtn"){ id: 'myBtn' }Praman strips view prefixes automatically
By.xpath("//button[@text='Save']"){ controlType: 'sap.m.Button', properties: { text: 'Save' } }Property matching replaces XPath
By.cssSelector(".sapMBtn"){ controlType: 'sap.m.Button' }Control type replaces CSS class matching
By.className("sapMInputBaseInner"){ controlType: 'sap.m.Input', id: 'myInput' }Target the control, not its inner DOM
By.linkText("Purchase Orders"){ controlType: 'sap.m.Link', properties: { text: 'Purchase Orders' } }Text property replaces link text
By.xpath("//div[@data-path]"){ bindingPath: { value: '/Vendor/Name' } }OData binding path matching
By.xpath with contains(){ id: /partialMatch/ }RegExp ID matching
By.xpath with ancestor axis{ ancestor: { controlType: 'sap.m.Panel', id: 'headerPanel' } }Ancestor selector replaces XPath ancestor axis
N/A{ searchOpenDialogs: true }Dialog-aware search (no Selenium equivalent)

:::tip Generated IDs Selenium tests for SAP UI5 apps commonly break because UI5 generates DOM IDs like __xmlview0--__button3. These IDs change across page reloads, UI5 versions, and theme switches. Praman's UI5Selector bypasses DOM IDs entirely by querying the UI5 control registry. :::

Config Mapping

Selenium Capabilities (Java)

ChromeOptions options = new ChromeOptions();
options.addArguments("--headless", "--window-size=1920,1080");
WebDriver driver = new ChromeDriver(options);
driver.manage().timeouts().implicitlyWait(Duration.ofSeconds(10));
driver.manage().timeouts().pageLoadTimeout(Duration.ofSeconds(60));

Praman Config (playwright.config.ts + praman.config.ts)

// playwright.config.ts
import { defineConfig, devices } from '@playwright/test';

export default defineConfig({
testDir: './tests',
timeout: 120_000,
retries: 1,
workers: process.env.CI ? 2 : 1,
use: {
baseURL: process.env.SAP_CLOUD_BASE_URL,
headless: true, // --headless
viewport: { width: 1920, height: 1080 }, // --window-size
trace: 'on-first-retry',
screenshot: 'only-on-failure',
},
projects: [
{ name: 'setup', testMatch: /auth-setup\.ts/, teardown: 'teardown' },
{ name: 'teardown', testMatch: /auth-teardown\.ts/ },
{
name: 'chromium',
dependencies: ['setup'],
use: { ...devices['Desktop Chrome'], storageState: '.auth/sap-session.json' },
},
],
});
// praman.config.ts
import { defineConfig } from 'playwright-praman';

export default defineConfig({
logLevel: 'info',
ui5WaitTimeout: 30_000, // Replaces implicitlyWait + WebDriverWait
controlDiscoveryTimeout: 10_000,
interactionStrategy: 'ui5-native',
auth: {
strategy: 'basic',
baseUrl: process.env.SAP_CLOUD_BASE_URL!,
username: process.env.SAP_CLOUD_USERNAME!,
password: process.env.SAP_CLOUD_PASSWORD!,
},
});

Page Object Model to Fixtures

Selenium teams almost universally use the Page Object Model. Praman replaces class-based POMs with Playwright's fixture system -- dependency injection with automatic teardown.

Selenium Page Object (Java)

public class PurchaseOrderPage {
@FindBy(id = "__xmlview0--vendorInput-inner") private WebElement vendorInput;
@FindBy(id = "__xmlview0--saveBtn") private WebElement saveButton;
@FindBy(css = ".sapMMsgStrip") private WebElement messageStrip;

public PurchaseOrderPage(WebDriver d) { PageFactory.initElements(d, this); }
public void fillVendor(String v) { vendorInput.clear(); vendorInput.sendKeys(v); }
public void save() { saveButton.click(); }
public String getMessage() { return messageStrip.getText(); }
}

// Test
PurchaseOrderPage po = new PurchaseOrderPage(driver);
po.fillVendor("100001");
po.save();
assertEquals("PO created", po.getMessage());

Praman Fixture Equivalent

import { test, expect } from 'playwright-praman';

test('create purchase order', async ({ ui5, ui5Navigation, ui5Footer }) => {
await test.step('Navigate', async () => {
await ui5Navigation.navigateToApp('PurchaseOrder-create');
});

await test.step('Fill vendor', async () => {
await ui5.fill({ id: 'vendorInput' }, '100001');
});

await test.step('Save', async () => {
await ui5Footer.clickSave();
});

await test.step('Verify success', async () => {
const message = await ui5.control({
controlType: 'sap.m.MessageStrip',
properties: { type: 'Success' },
searchOpenDialogs: true,
});
await expect(message).toBeUI5Visible();
});
});

For reusable logic across tests, extract helper functions instead of POM classes.

// helpers/po-helpers.ts
import type { ExtendedUI5Handler } from 'playwright-praman';

export async function fillPOHeader(
ui5: ExtendedUI5Handler,
data: { vendor: string; purchaseOrg: string; companyCode: string },
): Promise<void> {
await ui5.fill({ id: 'vendorInput' }, data.vendor);
await ui5.fill({ id: 'purchOrgInput' }, data.purchaseOrg);
await ui5.fill({ id: 'compCodeInput' }, data.companyCode);
}

Hybrid Approach During Migration

You do not need to rewrite everything at once. Praman's test and expect are extended versions of Playwright's -- page.locator() works alongside ui5.control() in the same test.

import { test, expect } from 'playwright-praman';

test('hybrid test', async ({ page, ui5, ui5Navigation }) => {
// Playwright handles login (before UI5 loads)
await test.step('Login', async () => {
await page.goto(process.env.SAP_CLOUD_BASE_URL!);
await page.locator('#USERNAME_FIELD input').fill('TESTUSER');
await page.locator('#PASSWORD_FIELD input').fill('secret');
await page.locator('#LOGIN_LINK').click();
await page.waitForURL('**/FioriLaunchpad*');
});

// Praman takes over once UI5 is loaded
await test.step('Navigate and verify', async () => {
await ui5Navigation.navigateToApp('PurchaseOrder-manage');
await expect(page).toHaveTitle(/Purchase Orders/);

const table = await ui5.control({ controlType: 'sap.m.Table', id: 'poTable' });
await expect(table).toHaveUI5RowCount({ min: 1 });
});
});

New Features Only in Praman

Praman includes capabilities that have no Selenium equivalent. These are worth adopting early.

6 Built-In Auth Strategies

No more custom login scripts or cookie injection. Praman supports Basic, BTP SAML, Office 365, Custom, API, and Certificate authentication out of the box.

test('after auth', async ({ ui5Navigation }) => {
await ui5Navigation.navigateToApp('PurchaseOrder-manage');
});

11 FLP Navigation Methods

test('navigation', async ({ ui5Navigation }) => {
await ui5Navigation.navigateToApp('PurchaseOrder-manage');
await ui5Navigation.navigateToTile('Create Purchase Order');
await ui5Navigation.navigateToIntent(
{ semanticObject: 'PurchaseOrder', action: 'create' },
{ plant: '1000' },
);
await ui5Navigation.navigateToHome();
await ui5Navigation.navigateBack();
});

Fiori Elements Helpers

Dedicated APIs for List Report and Object Page patterns -- no more fragile XPath chains.

test('FE list report', async ({ fe }) => {
await fe.listReport.setFilter('Status', 'Active');
await fe.listReport.search();
await fe.listReport.navigateToItem(0);
await fe.objectPage.clickEdit();
await fe.objectPage.clickSave();
});

AI-Powered Test Generation

test('AI discovery', async ({ pramanAI }) => {
const context = await pramanAI.discoverPage({ interactiveOnly: true });
const result = await pramanAI.agentic.generateTest(
'Create a purchase order for vendor 100001',
page,
);
});

Custom UI5 Matchers

10 UI5-specific Playwright matchers -- replacing verbose Selenium assert chains.

const button = await ui5.control({ id: 'saveBtn' });
await expect(button).toBeUI5Enabled();
await expect(button).toHaveUI5Text('Save');
await expect(button).toBeUI5Visible();

OData Model + HTTP Operations

test('OData', async ({ ui5 }) => {
const data = await ui5.odata.getModelData('/PurchaseOrders');
// HTTP-level CRUD with automatic CSRF handling
});

SM12 Lock Management

test('locks', async ({ flpLocks }) => {
await flpLocks.deleteAllLockEntries('TESTUSER');
// Auto-cleanup on teardown
});

Structured Error Codes

Every Praman error includes a machine-readable code, retryable flag, and suggestions[] array for self-healing tests. Selenium errors are raw WebDriverException messages with stack traces but no recovery guidance.

Step-by-Step Migration Checklist

  1. Install dependencies: npm install playwright-praman @playwright/test
  2. Install browsers: npx playwright install
  3. Create config files: praman.config.ts and playwright.config.ts (see Config Mapping)
  4. Set up auth: Create tests/auth-setup.ts using one of the 6 strategies
  5. Convert selectors: Replace By.id() / By.xpath() / By.css() with UI5Selector objects
  6. Remove explicit waits: Delete all WebDriverWait, Thread.sleep(), implicitlyWait -- Praman auto-waits
  7. Convert Page Objects: Replace POM classes with fixture destructuring and helper functions
  8. Convert assertions: Replace JUnit/TestNG assertEquals with Playwright expect + UI5 matchers
  9. Structure tests: Convert @Test methods to test() + test.step() blocks
  10. Adopt navigation: Replace driver.get() URL strings with ui5Navigation methods
  11. Remove driver management: Delete ChromeDriver setup, Grid config, and teardown code
  12. Run and verify: npx playwright test

Team Workflow Transition

From Java/Maven/Grid to TypeScript/npm/Playwright

WorkflowSelenium StackPraman Stack
LanguageJava (+ Kotlin, Python, C#)TypeScript
Build toolMaven / Gradlenpm
Test runnerJUnit / TestNGPlaywright Test (built-in)
Test structure@Test annotated methods in classestest() functions in .test.ts files
Page ObjectsJava classes with @FindByPlaywright fixtures + helper functions
Driver managementWebDriverManager / ChromeDrivernpx playwright install (fully automatic)
Parallel executionSelenium Grid + TestNG parallel suiteworkers: 4 in playwright.config.ts
CI/CDGrid server + nodes + Dockernpx playwright test (zero infrastructure)
ReportingAllure / ExtentReports pluginBuilt-in HTML report (npx playwright show-report)
Environment configMaven profiles / testng.xml.env files + playwright.config.ts projects
Version controlGit (test code) + driver binariesGit (everything, no binaries)
IDEIntelliJ / EclipseVS Code (recommended) / any IDE

Suggested Team Transition Plan

Week 1-2: Foundation

  • Install Node.js, VS Code, and Playwright on developer machines
  • Complete the Playwright Primer (2-3 hours per person)
  • Set up a Git repository for the test project

Week 3-4: First Tests

  • Convert 3-5 Selenium test classes from a single Fiori app to Praman tests
  • Set up authentication via setup projects (replaces Selenium login helpers)
  • Run tests locally and review the HTML report

Week 5-6: CI Integration

  • Add npx playwright test to your CI/CD pipeline (replaces Grid + Maven Surefire)
  • Configure screenshot: 'only-on-failure' and trace: 'on-first-retry'
  • Set up environment variables for SAP credentials in CI secrets

Week 7-8: Scale

  • Convert remaining Selenium test suites in priority order
  • Adopt Fiori Elements helpers (fe.listReport, fe.objectPage) for standard Fiori apps
  • Enable parallel execution with 2-4 workers (replaces Selenium Grid)
  • Decommission Selenium Grid infrastructure

Role Mapping

Selenium RolePraman Equivalent
SDET / Automation EngineerTest Engineer (writes .test.ts files)
Grid AdministratorNot needed (no infrastructure to manage)
Driver / Browser ManagerNot needed (npx playwright install)
Test Architect (POMs)Same role, designs fixtures and helper modules
QA Lead / ManagerReviews PRs, reads Playwright HTML reports, monitors CI

FAQ

How does Playwright's architecture differ from Selenium WebDriver?

Selenium communicates with the browser over the W3C WebDriver protocol via HTTP. Each command is a round-trip HTTP request to the driver server (ChromeDriver, GeckoDriver). Playwright uses the Chrome DevTools Protocol (CDP) or WebDriver BiDi over a persistent WebSocket connection. This means Playwright commands are faster, support event-driven waiting, and do not require separate driver binaries.

Can I migrate incrementally or do I need to rewrite all tests at once?

You can migrate incrementally. Start with a few high-value test classes, convert them to Praman tests, and run both Selenium and Playwright suites in parallel during the transition. Praman supports hybrid tests where page.locator() and ui5.control() coexist in the same file, so you can convert selectors one at a time.

Do I still need Selenium Grid for parallel execution?

No. Playwright has native parallel execution via the workers setting in playwright.config.ts. Each worker runs in its own isolated browser context. There is no grid infrastructure to install, configure, or maintain. Run npx playwright test on any machine or CI runner.

What happens to my Page Object Model classes?

Playwright uses fixtures instead of class-based Page Object Models. Fixtures provide dependency injection with automatic setup and teardown. You can extract reusable logic into helper functions (see the "Page Object Model to Fixtures" section above). The pattern is simpler and avoids the @FindBy annotation overhead.

Next steps