Intent API
The playwright-praman/intents sub-path provides high-level, business-oriented test operations for SAP S/4HANA modules. Instead of interacting
with individual UI5 controls, intent APIs express test steps in business terms.
Overview
import {
fillField,
clickButton,
selectOption,
assertField,
navigateAndSearch,
} from 'playwright-praman/intents';
import { procurement } from 'playwright-praman/intents';
The intent layer is built on two foundations:
- Core wrappers -- low-level building blocks that accept a UI5Handler slice and optional vocabulary lookup.
- Domain namespaces -- 9 SAP module-specific APIs that compose the core wrappers into business flows.
Core Wrappers
Core wrappers are the shared building blocks used by all domain intent functions. They accept a minimal UI5HandlerSlice interface, making them easy to unit test with mocks:
interface UI5HandlerSlice {
control(selector: UI5Selector, options?: { timeout?: number }): Promise<unknown>;
click(selector: UI5Selector): Promise<void>;
fill(selector: UI5Selector, value: string): Promise<void>;
select(selector: UI5Selector, key: string): Promise<void>;
getText(selector: UI5Selector): Promise<string>;
waitForUI5(timeout?: number): Promise<void>;
}
Available Core Wrappers
| Wrapper | Purpose |
|---|---|
fillField | Fill a UI5 input control, with optional vocabulary term resolution |
clickButton | Click a button by text label or selector |
selectOption | Select an option in a dropdown/ComboBox |
assertField | Assert the value of a field matches expected text |
navigateAndSearch | Navigate to an app via FLP and execute a search |
confirmAndWait | Click a confirmation button and wait for UI5 stability |
waitForSave | Wait for a save operation to complete (message toast) |
Vocabulary Integration
Core wrappers accept an optional VocabLookup to resolve business terms to UI5 selectors:
interface VocabLookup {
getFieldSelector(term: string, domain?: string): Promise<UI5Selector | undefined>;
}
// With vocabulary: "Vendor" resolves to { id: 'vendorInput' }
await fillField(ui5, vocab, 'Vendor', '100001', { domain: 'procurement' });
// Without vocabulary: pass a selector directly
await fillField(ui5, undefined, { id: 'vendorInput' }, '100001');
When a vocabulary term cannot be resolved, the wrapper returns an IntentResult with status: 'error' and helpful suggestions.
IntentResult Envelope
All intent functions return a typed IntentResult<T>:
interface IntentResult<T = void> {
readonly status: 'success' | 'error' | 'partial';
readonly data?: T;
readonly error?: { readonly code: string; readonly message: string };
readonly metadata: {
readonly duration: number;
readonly retryable: boolean;
readonly suggestions: string[];
readonly intentName: string;
readonly sapModule: string;
readonly stepsExecuted: string[];
};
}
Example usage:
const result = await procurement.createPurchaseOrder(ui5, vocab, {
vendor: '100001',
material: 'RAW-0001',
quantity: 100,
plant: '1000',
});
if (result.status === 'success') {
console.log(`PO created: ${result.data}`);
console.log(`Steps: ${result.metadata.stepsExecuted.join(' -> ')}`);
// Steps: navigate -> fillVendor -> fillMaterial -> fillQuantity -> save
}
IntentOptions
All domain functions accept optional IntentOptions:
interface IntentOptions {
skipNavigation?: boolean; // Skip FLP navigation (app already open)
timeout?: number; // Override default timeout (ms)
validateViaOData?: boolean; // Validate result via OData GET after save
overrides?: IntentOverrides; // Environment-specific customization
}
interface IntentOverrides {
appId?: string; // Custom FLP semantic object hash
saveButtonText?: string; // Localized save button label
fields?: Record<string, string>; // Localized field label mappings
}
// Skip navigation when already on the correct page
const result = await procurement.createPurchaseOrder(ui5, vocab, poData, {
skipNavigation: true,
timeout: 60_000,
});
IntentOverrides
Use overrides to make tests portable across SAP system landscapes with different configurations or languages:
// German-language SAP system
await procurement.createPurchaseOrder(ui5, ui5Nav, vocab, poData, {
overrides: {
appId: 'ZMM_PO-create',
saveButtonText: 'Sichern',
fields: { Vendor: 'Lieferant', Plant: 'Werk' },
},
});
All 9 intent domains support overrides. The override values take precedence over vocabulary-resolved defaults.
Domain Namespaces
Procurement (MM -- Materials Management)
import { procurement } from 'playwright-praman/intents';
// Create a purchase order
await procurement.createPurchaseOrder(ui5, vocab, {
vendor: '100001',
material: 'RAW-0001',
quantity: 100,
plant: '1000',
});
// Search purchase orders
await procurement.searchPurchaseOrders(ui5, vocab, { vendor: '100001' });
// Display purchase order details
await procurement.displayPurchaseOrder(ui5, vocab, '4500012345');
Sales (SD -- Sales and Distribution)
import { sales } from 'playwright-praman/intents';
// Create a sales order
await sales.createSalesOrder(ui5, vocab, {
customer: '200001',
material: 'FG-1000',
quantity: 50,
salesOrganization: '1000',
});
// Search sales orders
await sales.searchSalesOrders(ui5, vocab, { customer: '200001' });
Finance (FI -- Financial Accounting)
import { finance } from 'playwright-praman/intents';
// Post a journal entry
await finance.postJournalEntry(ui5, vocab, {
documentDate: '2026-02-20',
postingDate: '2026-02-20',
lineItems: [
{ glAccount: '400000', debitCredit: 'S', amount: 1000 },
{ glAccount: '110000', debitCredit: 'H', amount: 1000 },
],
});
// Post a vendor invoice
await finance.postVendorInvoice(ui5, vocab, {
vendor: '100001',
invoiceDate: '2026-02-20',
amount: 5000,
currency: 'EUR',
});
// Process a vendor payment
await finance.processPayment(ui5, vocab, {
vendor: '100001',
amount: 5000,
paymentDate: '2026-02-28',
});
Manufacturing (PP -- Production Planning)
import { manufacturing } from 'playwright-praman/intents';
// Create a production order
await manufacturing.createProductionOrder(ui5, vocab, {
material: 'FG-1000',
plant: '1000',
quantity: 50,
});
// Confirm a production order
await manufacturing.confirmProductionOrder(ui5, vocab, {
orderNumber: '1000012',
quantity: 50,
});
Master Data (MD -- Cross-Module)
import { masterData } from 'playwright-praman/intents';
// Create a vendor master
await masterData.createVendor(ui5, vocab, {
name: 'Acme GmbH',
country: 'DE',
taxId: 'DE123456789',
});
// Create a customer master
await masterData.createCustomer(ui5, vocab, {
name: 'Globex Corp',
country: 'US',
salesOrganization: '1000',
});
// Create a material master
await masterData.createMaterial(ui5, vocab, {
materialNumber: 'RAW-0001',
description: 'Raw material A',
materialType: 'ROH',
baseUnit: 'KG',
});
Quality Management (QM -- Quality Management)
import { quality } from 'playwright-praman/intents';
// Create an inspection lot
await quality.createInspectionLot(ui5, ui5Nav, vocab, {
material: 'RAW-0001',
plant: '1000',
inspectionType: '01',
});
// Record inspection results
await quality.recordResults(ui5, ui5Nav, vocab, {
inspectionLot: '000012345678',
result: '10.5',
});
// Create a quality notification
await quality.createQualityNotification(ui5, ui5Nav, vocab, {
notificationType: 'Q1',
description: 'Surface defect on batch 2026-03',
});
Warehouse Management (WM -- Warehouse Management)
import { warehouse } from 'playwright-praman/intents';
// Create a goods movement
await warehouse.createGoodsMovement(ui5, ui5Nav, vocab, {
movementType: '101',
material: 'RAW-0001',
plant: '1000',
quantity: '500',
});
// Create a transfer order
await warehouse.createTransferOrder(ui5, ui5Nav, vocab, {
warehouseNumber: '001',
material: 'RAW-0001',
quantity: '100',
});
Asset Management (AM -- Fixed Assets)
import { assetManagement } from 'playwright-praman/intents';
// Acquire a fixed asset
await assetManagement.acquireAsset(ui5, ui5Nav, vocab, {
assetClass: '1000',
description: 'CNC Machine Model X',
companyCode: '1000',
});
// Retire an asset
await assetManagement.retireAsset(ui5, ui5Nav, vocab, {
assetNumber: '000000001000',
retirementDate: '2026-12-31',
});
// Transfer an asset between cost centers
await assetManagement.transferAsset(ui5, ui5Nav, vocab, {
assetNumber: '000000001000',
targetCostCenter: '2000',
});
Human Resources (HR -- Personnel Administration)
import { hr } from 'playwright-praman/intents';
// Create an employee record
await hr.createEmployee(ui5, ui5Nav, vocab, {
firstName: 'Max',
lastName: 'Mustermann',
personnelArea: '1000',
});
// Record working time
await hr.recordTime(ui5, ui5Nav, vocab, {
personnelNumber: '00000001',
date: '2026-10-04',
hours: '8.0',
});
// Request absence/leave
await hr.requestAbsence(ui5, ui5Nav, vocab, {
absenceType: 'Vacation',
startDate: '2026-11-01',
endDate: '2026-11-05',
});
Data Shapes
Each domain defines typed data interfaces:
| Type | Domain | Purpose |
|---|---|---|
JournalEntryData | FI | General-ledger journal entry posting |
VendorInvoiceData | FI | Accounts-payable vendor invoice |
PaymentData | FI | Outgoing payment processing |
ProductionOrderData | PP | Shop-floor order creation |
ProductionConfirmationData | PP | Production order confirmation |
VendorMasterData | MD | Vendor master data creation |
CustomerMasterData | MD | Customer master data creation |
MaterialMasterData | MD | Material master data creation |
InspectionLotData | QM | Quality inspection lot creation |
ResultsRecordingData | QM | Inspection results recording |
QualityNotificationData | QM | Quality notification creation |
GoodsMovementData | WM | Warehouse goods movement |
TransferOrderData | WM | Warehouse transfer order |
AssetAcquisitionData | AM | Fixed asset acquisition |
AssetRetirementData | AM | Fixed asset retirement |
AssetTransferData | AM | Fixed asset transfer |
EmployeeData | HR | Employee record creation |
TimeRecordingData | HR | Working time recording |
AbsenceRequestData | HR | Leave/absence request |
Architecture
The intent layer follows strict dependency rules:
Domain functions (procurement.ts, sales.ts, quality.ts, warehouse.ts, etc.)
|
v
Core wrappers (core-wrappers.ts)
|
v
UI5HandlerSlice (structural interface) + VocabLookup (optional)
- Core wrappers do NOT import from any domain file (no circular dependencies).
- Domain functions compose core wrappers into SAP business flows.
- The
UI5HandlerSliceis a structural sub-type -- it does not import the fullUI5Handlerclass. - The
VocabLookupinterface is declared inline to avoid hard dependencies on the vocabulary module.