Skip to main content
Version: 1.x

Quick Reference

Everything you need on one page. Copy-paste ready.


Imports

// Standard test — includes all fixtures
import { test, expect } from 'playwright-praman';

// Selective composition — only what you need
import { coreTest, authTest, moduleTest } from 'playwright-praman';
import { mergeTests } from '@playwright/test';
const test = mergeTests(coreTest, authTest);

Selectors

By ID

await ui5.control({ id: 'myButton' });
await ui5.control({ id: /partial.*match/ });

By Control Type + Properties

await ui5.control({
controlType: 'sap.m.Button',
properties: { text: 'Save', enabled: true },
});

By Binding Path (OData)

await ui5.control({
controlType: 'sap.m.Input',
bindingPath: { value: '/PurchaseOrder/Vendor' },
});

By i18n Text

await ui5.control({
controlType: 'sap.m.Button',
i18NText: { text: 'SAVE_BUTTON' },
});

Scoped by View

await ui5.control({
id: 'myInput',
viewName: 'sap.app.view.Detail',
});

Ancestor / Descendant

await ui5.control({
controlType: 'sap.m.Input',
ancestor: { controlType: 'sap.m.Dialog' },
});

Sub-Control Interaction

// ComboBox dropdown arrow
await ui5.control({
controlType: 'sap.m.ComboBox',
id: 'myCombo',
interaction: { idSuffix: 'arrow' },
});

Dialog Controls (Critical)

// ❌ Won't find controls inside dialogs
await ui5.control({ id: 'dialogBtn' });

// ✅ Must set searchOpenDialogs: true
await ui5.control({ id: 'dialogBtn', searchOpenDialogs: true });

Locator String Syntax

// JSON format
page.locator('ui5={"controlType":"sap.m.Button","properties":{"text":"Save"}}');

// CSS-like format
page.locator("ui5=sap.m.Button[text='Save']");
page.locator('ui5=sap.m.Input:labeled("Vendor")');

Common Control Types

ControlType String
Buttonsap.m.Button
Inputsap.m.Input
Selectsap.m.Select
ComboBoxsap.m.ComboBox
DatePickersap.m.DatePicker
CheckBoxsap.m.CheckBox
TextAreasap.m.TextArea
Responsive Tablesap.m.Table
Grid Tablesap.ui.table.Table
SmartTablesap.ui.comp.smarttable.SmartTable
SmartFieldsap.ui.comp.smartfield.SmartField
Dialogsap.m.Dialog
GenericTilesap.m.GenericTile
IconTabBarsap.m.IconTabBar
Listsap.m.List

Interaction Shortcuts

// Click
await ui5.click({ id: 'submitBtn' });

// Type into input (setValue + fireChange + waitForUI5)
await ui5.fill({ id: 'nameInput' }, 'John Doe');

// Select dropdown value
await ui5.select({ id: 'countrySelect' }, 'US');

// Toggle checkbox
await ui5.check({ id: 'agreeCheckbox' });

// Clear field
await ui5.clear({ id: 'searchField' });

// Press (firePress on control)
await ui5.press({ id: 'goButton' });
Input Pattern

ui5.fill() performs setValue + fireChange + waitForUI5 atomically. Never call setValue() alone — UI5 change events won't fire.


Fixtures

Core (test-scoped)

FixturePurpose
ui5Control discovery, .table, .dialog, .date, .odata sub-namespaces
ui5NavigationFLP navigation (9 methods)
btpWorkZoneDual-frame WorkZone manager
sapAuthAuthentication (6 strategies)
feFiori Elements (.listReport, .objectPage, .table, .list)
pramanAIAI discovery, agentic handler, vocabulary
intentBusiness intents (.procurement, .sales, .finance, .manufacturing, .masterData)
ui5ShellShell header (home, user menu)
ui5FooterFooter bar (Save, Edit, Delete)
flpLocksSM12 lock management + auto-cleanup
flpSettingsUser settings (language, date format)
testDataTemplate-based data generation + auto-cleanup
pramanLoggerTest-scoped pino logger

Infrastructure (worker-scoped)

FixturePurpose
pramanConfigFrozen config (loaded once per worker)
rootLoggerWorker-scoped root logger
tracerOpenTelemetry tracer (NoOp when disabled)

CLI Integration (test-scoped)

FixturePurpose
browserBindExpose browser to CLI agents (PRAMAN_BIND=1)
screencastChapter markers, action overlays

Auto-Fixtures (fire automatically)

playwrightCompat · selectorRegistration · matcherRegistration · requestInterceptor · ui5Stability


test('navigate', async ({ ui5Navigation, btpWorkZone }) => {
// By tile name
await ui5Navigation.navigateToTile('Create Purchase Order');

// By app ID
await ui5Navigation.navigateToApp('PurchaseOrder-manage');

// By intent with parameters
await ui5Navigation.navigateToIntent(
{ semanticObject: 'PurchaseOrder', action: 'create' },
{ plant: '1000' },
);

// Basic navigation
await ui5Navigation.navigateToHome();
await ui5Navigation.navigateBack();
await ui5Navigation.navigateForward();
const hash = await ui5Navigation.getCurrentHash();

// WorkZone spaces
await btpWorkZone.navigateToSpace('HR');
await btpWorkZone.navigateToSectionLink('Payroll');
});

Tables

test('tables', async ({ ui5 }) => {
// Row count
const count = await ui5.table.getRowCount('myTable');

// Read data
const data = await ui5.table.getTableData('myTable');
const cell = await ui5.table.getTableCellValue('myTable', 0, 'Vendor');
const cols = await ui5.table.getColumnNames('myTable');

// Select rows
await ui5.table.selectTableRow('myTable', 0);
await ui5.table.selectAllTableRows('myTable');
const selected = await ui5.table.getSelectedRows('myTable');

// Find row by value
await ui5.table.findRowByValues('myTable', { Vendor: '100001' });
await ui5.table.selectRowByValues('myTable', { Status: 'Active' });

// Write
await ui5.table.setTableCellValue('myTable', 0, 'Quantity', '10');

// Filter and sort
await ui5.table.filterByColumn('myTable', 'Status', 'Active');
await ui5.table.sortByColumn('myTable', 'CreatedAt', false);

// Wait for data to load
await ui5.table.waitForTableData('myTable');

// Detect table type
const type = await ui5.table.detectTableType('myTable');
// Returns: 'smart' | 'responsive' | 'grid'
});

Dialogs

test('dialogs', async ({ ui5 }) => {
// Trigger dialog
await ui5.press({ id: 'createBtn' });

// Wait for it
await ui5.dialog.waitForDialog();

// Interact (always use searchOpenDialogs!)
await ui5.fill(
{ id: 'dialog--nameInput', searchOpenDialogs: true },
'Test Value',
);

// Confirm or dismiss
await ui5.dialog.confirm(); // Clicks primary action
await ui5.dialog.dismiss(); // Clicks cancel/close
await ui5.dialog.confirmDialog('OK'); // Specific button text

// Query state
const isOpen = await ui5.dialog.isDialogOpen();
const buttons = await ui5.dialog.getDialogButtons();
const dialogs = await ui5.dialog.getOpenDialogs();

// Wait for close
await ui5.dialog.waitForDialogClosed();
});

Date / Time

test('dates', async ({ ui5 }) => {
// DatePicker
await ui5.date.setDatePickerValue('startDate', new Date('2026-01-15'));
const value = await ui5.date.getDatePickerValue('startDate');

// TimePicker
await ui5.date.setTimePickerValue('startTime', '14:30');
const time = await ui5.date.getTimePickerValue('startTime');

// DateRangeSelection
await ui5.date.setDateRangeSelection('range', {
from: new Date('2026-01-01'),
to: new Date('2026-12-31'),
});
const range = await ui5.date.getDateRangeSelection('range');

// Set + validate in one call
await ui5.date.setAndValidateDate('deliveryDate', new Date('2026-06-01'), '06/01/2026');
});

OData

test('odata', async ({ ui5 }) => {
// Model-level reads
const data = await ui5.odata.getModelData('/PurchaseOrders');
const count = await ui5.odata.getEntityCount('PurchaseOrder');
const prop = await ui5.odata.getModelProperty('PoList', 0);

// HTTP-level CRUD
const token = await ui5.odata.fetchCSRFToken();
const entities = await ui5.odata.queryEntities('PurchaseOrder', {
filter: "Vendor eq '100001'",
});
await ui5.odata.createEntity('PurchaseOrder', { vendor: '100001' });
await ui5.odata.updateEntity('PurchaseOrder', 'PO-001', { status: 'Complete' });
await ui5.odata.deleteEntity('PurchaseOrder', 'PO-001');
});

Fiori Elements

test('fiori elements', async ({ fe }) => {
// List Report
await fe.listReport.setFilter('Status', 'Active');
await fe.listReport.search();
await fe.listReport.navigateToItem(0);

// Object Page
const title = await fe.objectPage.getHeaderTitle();
await fe.objectPage.clickEdit();
await fe.objectPage.clickSave();
});

Custom Matchers

// Control state
await expect(control).toBeUI5Visible();
await expect(control).toBeUI5Enabled();
await expect(control).toBeUI5Editable();

// Control content
await expect(control).toHaveUI5Text('Expected Text');
await expect(control).toHaveUI5Property('value', 'expected');
await expect(control).toHaveUI5ItemCount(5);

// Table state
await expect(table).toHaveUI5Rows(10);

// Negative
await expect(control).not.toBeUI5Visible();

Error Codes (Top Categories)

Every error includes code, message, attempted, retryable, suggestions[], and docsUrl.

CategoryCodesCommon Causes
ConfigERR_CONFIG_INVALID, _NOT_FOUND, _PARSEBad config file, missing file, syntax error
BridgeERR_BRIDGE_TIMEOUT, _INJECTION, _NOT_READYUI5 not loaded, injection failed, version mismatch
ControlERR_CONTROL_NOT_FOUND, _NOT_VISIBLE, _NOT_ENABLEDWrong selector, element hidden/disabled
AuthERR_AUTH_FAILED, _TIMEOUT, _SESSION_EXPIREDBad credentials, slow login, session expired
NavigationERR_NAV_TILE_NOT_FOUND, _ROUTE_FAILED, _TIMEOUTWrong tile name, FLP not loaded
ODataERR_ODATA_REQUEST_FAILED, _PARSE, _CSRFService error, bad response, token expired
SelectorERR_SELECTOR_INVALID, _AMBIGUOUS, _PARSEBad syntax, multiple matches
TimeoutERR_TIMEOUT_UI5_STABLE, _CONTROL_DISCOVERYSlow app, long-running OData calls
import { ControlError, TimeoutError } from 'playwright-praman';

try {
await ui5.control({ id: 'missing' });
} catch (error) {
if (error instanceof ControlError && error.retryable) {
// retry logic
}
console.error(error.toUserMessage()); // Human-readable
const ctx = error.toAIContext(); // AI-consumable
}

Configuration

Minimal praman.config.ts

import { defineConfig } from 'playwright-praman';

export default defineConfig({
logLevel: 'info',
ui5WaitTimeout: 30_000,
controlDiscoveryTimeout: 10_000,
interactionStrategy: 'ui5-native',
auth: {
strategy: 'basic',
baseUrl: 'https://your-sap.example.com',
username: process.env.SAP_USER!,
password: process.env.SAP_PASS!,
client: '100',
language: 'EN',
},
});

Environment Variable Overrides

VariableDefault
PRAMAN_LOG_LEVELinfo
PRAMAN_UI5_WAIT_TIMEOUT30000
PRAMAN_CONTROL_DISCOVERY_TIMEOUT10000
PRAMAN_INTERACTION_STRATEGYui5-native
PRAMAN_SKIP_STABILITY_WAITfalse

Precedence (highest wins)

  1. Per-call options: ui5.control({ ... }, { timeout: 5000 })
  2. Environment variables
  3. praman.config.ts
  4. Schema defaults

Auth Strategies

StrategyUse Case
basicSAP GUI / Fiori on-premise (user + password + client)
btp-samlBTP with SAML/IDP
office365Azure AD / Entra ID
certificateX.509 client certificate
api-keyAPI-only authentication
customCustom login flow

Test Structure (Playwright Best Practices)

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

test.describe('Purchase Order', () => {
test('create PO end-to-end', async ({ ui5, ui5Navigation, fe }) => {
await test.step('Navigate to Create PO', async () => {
await ui5Navigation.navigateToApp('PurchaseOrder-create');
});

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

await test.step('Save and verify', async () => {
await ui5.press({ id: 'saveBtn' });
const msg = await ui5.control({
controlType: 'sap.m.MessageStrip',
properties: { type: 'Success' },
});
await expect(msg).toHaveUI5Text(/Purchase Order \d+ created/);
});
});
});

CLI Commands

# Run tests
npx playwright test

# Run specific test file
npx playwright test tests/po-create.spec.ts

# Debug mode (headed + inspector)
npx playwright test --debug

# Generate report
npx playwright show-report

# Praman CLI inspector
npx praman inspect

# Praman capabilities
npx praman capabilities --agent