Skip to main content

Approach for SAP S/4 HANA Test Automation

Aligned to SAP Activate. Quality automation starts in Realize.

SAP Activate Methodology
Discover
Prepare
Explore
Realize
Deploy
Run
AI + Playwright + Praman
01
Business Flow
02
Test Scenario Generation
03
Connect OData & Test Data
04
Explore SAP App
05
Create Test Cases
06
Create Test Script
07
Execution Reports
08
Pull in SAP Cloud ALM
Step 1 of 8
Business Flow
Import Signavio / BPMN process definitions as test scope

How Playwright + Praman Plugin Works: End-to-End Flow

From seed file authentication through live SAP discovery to compliant test artifacts — fully orchestrated by AI agents.

SSeed FilePPlaywright MCP / CLIAPraman SAP PlannerFSAP Fiori / ODataTTest Artifacts1Auth & Browser Session2Live Browser Context3Navigate & Discover4Control Metadata + OData Schema5Generate Test Plan6Generate Test Script7Execute & Validate8Evidence Artifacts
1Seed File Playwright MCP / CLI
Auth & Browser Session
Seed file provides SAP credentials + opens authenticated browser via Playwright MCP or Playwright CLI

End-to-End Orchestration

Test code Fixtures Bridge UI5 Control Resolution Smart Waits & Retries Assertions.
Traced from the real BOM E2E Gold Standard test through 6 architectural layers.

TTest Codebom-e2e.spec.tsFFixturesui5, sapAuth, odataBUI5 Bridgewindow.__praman_bridgeUUI5 Runtimesap.ui.getCore()PControl ProxyUI5ControlProxyAAssertionsexpect + matchers1import { test, expect }2Inject Bridge33-Tier Control Lookup4ControlDiscoveryResult5Create Proxy6Call UI5 Methods7Execute in Browser8Smart Wait9Chainable Result10Assert & Report
>import { test, expect } from 'playwright-praman'
1import { test, expect }Test Code Fixtures
Test destructures ui5 fixture from merged fixture chain. mergeTests() composes auth, navigation, stability, Fiori Elements, and AI fixtures into a single test object.

Architecture Deep Dive

Comprehensive technical documentation verified against source code. 180 source files, 551 type definitions, 6-layer architecture — every number counted, not estimated.

Metrics at a Glance

All numbers verified against source code. No estimates — every metric is counted from the actual codebase.

97KTotal LOC
46.4KSource LOC
50.6KTest LOC
180Source Files
392Functions
275Exported Functions
551Type Definitions
389Interfaces
162Type Aliases
11Fixture Modules
10Custom Matchers
13Proxy Overrides
10Bridge Scripts
13Error Classes
3Interaction Strategies
199UI5 Control Types
6Sub-path Exports
11ESLint Plugins
14Source Directories
9Return Type Classes
8Proxy Trap Steps

Testing & Coverage

162Unit Test Files
3,442Test Cases
758Test Suites
9E2E Spec Files
99.1%Stmt Coverage
95.6%Branch Coverage
99.1%Function Coverage
99.3%Line Coverage

Core Architecture Layers

Praman follows a strict 6-layer architecture. Lower layers never import from higher layers. Each layer has a well-defined responsibility and communicates only with its adjacent layers.

Layer 5: AI Layerai/, intents/, vocabulary/PramanAI, Intents, Vocabulary, AI HealingLayer 4: Fixturesfixtures/, matchers/11 fixtures via mergeTests(), 10 custom matchersLayer 3: Typed Proxyproxy/UI5ControlProxy, 8-step get trap, 13 built-in overridesLayer 2: Bridge Adaptersbridge/, selectors/10 browser scripts, 3 strategies, injection engineLayer 1: Core Infrastructurecore/, auth/, modules/Errors, Config, Logging, Types, Selectors, Wait Helpers
Layer 1Core Infrastructure

Foundation layer: 13 typed error classes extending PramanError with structured code, attempted, retryable, and suggestions[] fields. Config system using Readonly<PramanConfig>. Pino-based structured logging (no console.log). Wait helpers: waitForUI5Stable(), briefDOMSettle(), waitForUI5Bootstrap(). 551 type definitions (389 interfaces + 162 type aliases).

Layer 2Bridge Adapters

10 browser scripts injected via page.evaluate(). Idempotent injection using WeakSet ensureBridgeInjected(page) sets up window.__praman_bridge with objectMap, getById(), and 3-tier control lookup. 3 interaction strategies: UI5Native (firePress/fireSelect/fireTap fallback chain), DomFirst (DOM click first), OPA5 (RecordReplay-based).

Layer 3Typed Proxy

ES Proxy with 8-step get trap resolution. 13 built-in method overrides (getId, getControlType, press, enterText, select, exec, getAggregation, toString, toJSON, renewWebElementReference, getControlMetadata, getControlInfoFull, retrieveMembers). 9-type return classification system. Fluent chaining via createChainableResult(). Blacklist enforcement prevents calling dangerous methods.

Layer 4Fixtures

11 fixture modules composed via Playwright's mergeTests(): module, auth, nav, stability, fe, ai, intent, shellFooter, flpLocks, flpSettings, testData. 10 custom UI5 matchers: checkUI5Text, checkUI5Visible, checkUI5Enabled, checkUI5Property, checkUI5ValueState, checkUI5Binding, checkUI5ControlType, checkUI5RowCount, checkUI5CellText, checkUI5SelectedRows.

Layer 5AI Layer

AI integration with optional dependencies (@anthropic-ai/sdk, openai). Intent-based test generation, vocabulary system for SAP domain terms, AI-powered test healing. Exports via sub-paths: playwright-praman/ai, playwright-praman/intents, playwright-praman/vocabulary.

Core Design Principles

PrincipleImplementationEnforcement
Strict TypeScriptNo `any`, no `as unknown as T`. 551 types, exactOptionalPropertyTypes, verbatimModuleSyntaxtypescript-eslint strict type-checked
Immutable ConfigReadonly<PramanConfig> — never mutated at runtimeTypeScript compiler + code review
Structured Errors14 error classes with code, attempted, retryable, suggestions[]PramanError base class enforcement
No console.logPino structured logging onlyESLint rule + code review
No page.waitForTimeout()Smart waits: waitForUI5Stable(), briefDOMSettle()eslint-plugin-playwright ban list
ESM-firstimport/export only, node: prefix, .js extensionseslint-plugin-n, eslint-plugin-import-x
Layer IsolationLower layers never import higher layerseslint-plugin-import-x boundaries
Web-first AssertionsPlaywright expect + custom UI5 matcherseslint-plugin-playwright
Cross-platformnode:path, node:fs/promises, no hardcoded separatorsCI 3-OS matrix (Linux, macOS, Windows)
Per-file CoverageTier 1: 100%, Tier 2: 95%, Tier 3: 90% — no file hides behind averages@vitest/coverage-v8 perFile: true
TSDoc DocumentationMicrosoft TSDoc standard, custom tags (@intent, @guarantee, @capability)eslint-plugin-tsdoc with syntax: error
Security First@microsoft/eslint-plugin-sdl, eslint-plugin-security, SHA-pinned Actions11 ESLint plugins, zero tolerance

Data Flow

The data flow traces a single UI5 interaction from test code through all 5 architecture layers and back. Non-serializable objects are stored in the bridge's objectMap with UUID keys for later retrieval.

1
Test Code
2
Fixtures
3
Bridge Injection
4
Browser Context
5
Control Proxy
6
page.evaluate()
7
UI5 Runtime
8
Return Handler
9
Assertions
10
Report

Flow Steps

  1. Test Code calls fixture method (e.g., ui5.control(selector))
  2. Fixtures ensure bridge is injected (idempotent via WeakSet), then invoke bridge
  3. Bridge Injection sets up window.__praman_bridge with objectMap + getById()
  4. Browser Context executes 3-tier control discovery in the live SAP application
  5. Control Proxy wraps the discovered control with ES Proxy for method interception
  6. page.evaluate() routes each method call through bridge scripts to the UI5 runtime
  7. Return Handler classifies the result into one of 9 types, creates sub-proxies for elements
  8. Assertions verify the result using Playwright expect + 10 custom UI5 matchers

Directory Structure

PathDescription
src/ai/AI integration (Anthropic, OpenAI)
src/auth/SAP authentication flows
src/bridge/Browser bridge + injection engine
└─ bridge/browser-scripts/10 scripts evaluated in browser context
└─ bridge/interaction-strategies/3 strategies (UI5Native, DomFirst, OPA5)
src/cli/CLI tooling
src/core/Foundation: errors, config, logging, types
└─ core/constants/Control types (199), timeouts, defaults
└─ core/errors/14 error classes, 67 error codes
└─ core/logging/Pino structured logger
└─ core/types/551 type definitions
└─ core/utils/Wait helpers, path helpers, compat
src/fe/SAP Fiori Elements support
src/fixtures/11 Playwright fixture modules
src/intents/Intent-based test generation
src/matchers/10 custom UI5 matchers
src/modules/Module system
src/proxy/UI5ControlProxy (8-step get trap)
src/reporters/Custom Playwright reporters
src/selectors/UI5 selector engine
src/vocabulary/SAP domain vocabulary
src/index.tsMain entry: exports test, expect
src/version.tsPackage version

Package Exports

6 sub-path exports with dual ESM + CJS output. Every export is validated by @arethetypeswrong/cli (attw) to ensure correct resolution in all bundlers.

Import PathESMCJSContents
playwright-praman./dist/index.js./dist/index.cjstest, expect, fixtures, proxy, matchers
playwright-praman/ai./dist/ai/index.js./dist/ai/index.cjsAI integration, healing, LLM adapters
playwright-praman/intents./dist/intents/index.js./dist/intents/index.cjsIntent-based test generation
playwright-praman/vocabulary./dist/vocabulary/index.js./dist/vocabulary/index.cjsSAP domain vocabulary system
playwright-praman/fe./dist/fe/index.js./dist/fe/index.cjsSAP Fiori Elements helpers
playwright-praman/reporters./dist/reporters/index.js./dist/reporters/index.cjsCustom Playwright reporters

Built by tsup with format: ['esm','cjs' ], cjsInterop: true, shims: true. Each export includes matching .d.ts and .d.cts type declaration files.

Proxy Architecture

The UI5ControlProxy is the central abstraction that makes SAP UI5 controls feel like local TypeScript objects. It uses an ES Proxy get trap that resolves property access through an 8-step pipeline.

1
Symbol?
toPrimitive → string, others → undefined
2
Anti-thenable?
then/catch/finally → undefined
3
Direct prop?
id, controlType → local state
4
Built-in?
13 overrides (press, setValue, getId…)
5
Blacklisted?
throwIfBlacklisted → ControlError
6
Dynamic forwarder
getOrCreateForwarder(prop) → cached
7
page.evaluate()
Execute method in browser via bridge
8
9-type return
Classify → proxy / primitive / array

13 Built-in Method Overrides

These methods are intercepted by resolveKnownProperty() and handled specially — they do not go through the generic dynamic method forwarder.

getId()
Returns local state synchronously
getControlType()
Returns local state synchronously
getAggregation(name)
Returns array of sub-proxies
press()
Delegates to interaction strategy
enterText(text)
Delegates to interaction strategy
select(value)
Delegates to interaction strategy
exec(fn)
Evaluates user function in browser
toString()
Returns proxy display string
toJSON()
Returns proxy display string
renewWebElementReference()
Checks control existence
getControlMetadata()
Class name, props, aggregations
getControlInfoFull()
Full introspection + prototype
retrieveMembers()
Method names from prototype chain

Nine-Type Return Detection System

When a method is executed in the browser via page.evaluate(), the bridge classifies the return value into one of 9 types defined in BridgeReturnType. Each type triggers a different handler in the proxy.

#TypeConditionProxy Action
1resultPrimitive value (string, number, boolean)Returns raw value directly
2emptyvoid / undefined / null returnReturns undefined, enables chaining
3elementExisting UI5 control (same control ref)Wraps in new sub-proxy with control metadata
4newElementNewly discovered control referenceCreates fresh proxy with full discovery
5aggregationArray of child controlsReturns array of sub-proxies, each fully typed
6objectNon-control UI5 object (model, binding)Stored in objectMap with UUID, returns accessor proxy
7objectArrayArray of non-control UI5 objectsEach stored with UUID in objectMap
8noneUnclassified return (logged as warning)Returns undefined with diagnostic logging
9unknownInstance check failedReturns undefined with diagnostic logging

Three-Tier Control Discovery

Control discovery is the mechanism by which Praman locates a UI5 control in the browser. The 3-tier approach ensures compatibility across UI5 versions (1.71 through 2.x) and handles complex selectors.

Tier 1: bridge.getById() — Exact ID Match

Direct lookup by control ID. Fastest path for full ID selectors. Skipped when forceRegistryScan is true. Internally, bridge.getById() itself chains through 3 version-aware APIs (D19):

1aElement.getElementById(id)
UI5 2.x
1bElementRegistry.get(id)
UI5 >= 1.120
1csap.ui.getCore().byId(id)
UI5 < 1.120 (legacy)

Each API is tried in order — first non-null result wins. This ensures compatibility from UI5 1.71 through 2.x.

Tier 2: Registry Scan — Enhanced Matching (GAP-02)

Iterates Element.registry.all() with support for: exact ID, suffix (--id), RegExp (/pattern/), controlType, properties, viewName, bindingPath. Prefers visible controls (GAP-21) — when multiple matches exist, visible controls are selected first.

API: sap.ui.core.Element.registry.all() with fallback to sap.ui.core.ElementRegistry
Tier 3: RecordReplay — Selector-based

Uses RecordReplay.findDOMElementByControlSelector for controlType + properties selectors. Maps DOM element back to UI5 control via version-aware conversion:

Element.closestTo(dom)
UI5 > 1.106 — primary
jQuery(dom).control(0)
Legacy jQuery fallback

Execution Strategy Options

Praman provides 3 interaction strategies for invoking UI5 control methods. The strategy is selected per-test or per-action, giving fine-grained control over how interactions are executed.

StrategyApproachFallback ChainBest For
UI5NativeCalls UI5 control API methods directly in browser contextfirePress()fireSelect() fireTap() → DOM clickStandard SAP Fiori apps, most reliable
DomFirstPerforms DOM interaction first, falls back to UI5 APIDOM click()firePress() fireTap()Hybrid apps, custom controls, non-standard handlers
OPA5Uses SAP OPA5 test framework's RecordReplay engineRecordReplay.interactWithControl() → DOM fallbackComplex controls, SmartFields, value helps

Technology Stack

Runtime Dependencies

@playwright/test>=1.57.0 <2.0.0 (peer)
commander14.0.3 — CLI framework
pino10.3.1 — structured logging
zod4.3.6 — schema validation

Optional AI Dependencies

@anthropic-ai/sdk>=0.78.0 (optional peer)
openai>=6.22.0 (optional peer)
@opentelemetry/api>=1.9.0 (optional peer)
@opentelemetry/sdk-node>=0.212.0 (optional peer)

Build & Quality Tooling

TypeScript5.9.3 — strict mode
tsup8.5.1 — dual ESM+CJS build
Vitest4.0.18 — unit testing
@vitest/coverage-v84.0.18 — per-file coverage
ESLint11 plugins, zero tolerance
attw0.18.2 — export validation
API Extractor7.56.3 — API surface
TypeDoc0.28.17 — docs generation
knip5.83.1 — dead code detection
cspell9.6.4 — spell checking

Security & Compliance

Multi-layered security posture verified against actual CI workflows, ESLint config, package.json scripts, and GitHub repository settings. Every item below is implemented and enforced in code.

SBOM & Supply Chain

MeasureTool / ImplementationDetails
SBOM Generation@cyclonedx/cyclonedx-npm 4.1.2CycloneDX 1.5 JSON output; npm run generate:sbom; auto-generated in release pipeline
npm Provenancenpm publish --provenanceOIDC-signed provenance attestation on every npm publish; id-token: write in release.yml
SHA-Pinned ActionsFull SHA commit hashesAll actions in ci.yml, release.yml, docs.yml pinned to exact SHAs (not floating tags)
Dependency Lockfilepackage-lock.json v3lockfileVersion 3 with SHA-512 integrity hashes; npm ci enforced in all CI steps
Dependabot.github/dependabot.ymlWeekly updates for npm (prod: patch-only, dev: minor+patch) and github-actions ecosystems
License Inventorylicense-report.json + ScanCodeFull dependency license audit; ScanCode Toolkit 32.0.2 scan of src/ for copyright/license detection
NOTICE FileRoot NOTICE + LICENSEApache 2.0 license; NOTICE with attribution; both published to npm via files field

Static Analysis & Code Security

MeasureTool / ImplementationDetails
Microsoft SDL@microsoft/eslint-plugin-sdl 1.1.08 SDL rules: no-insecure-url, no-inner-html, no-postmessage-star-origin, etc.
OWASP Securityeslint-plugin-security 3.0.112 rules: detect-eval, detect-unsafe-regex, detect-child-process, detect-non-literal-regexp
CodeQL Analysisgithub/codeql-action (CI)Weekly + PR scans with security-extended query suite for JavaScript/TypeScript
npm Auditnpm audit --audit-level=highRuns in CI security job on every push/PR; --omit=dev excludes devDependencies
Code Qualityeslint-plugin-sonarjs 3.0.7Detects cognitive complexity, code duplication, code smells
Dead Codeknip 5.83.1Eliminates unused exports, dependencies, files from production surface
TypeScript Strictstrict + noUncheckedIndexedAccessNo any, exactOptionalPropertyTypes, verbatimModuleSyntax, isolatedModules

Credential & Runtime Security

MeasureTool / ImplementationDetails
Log RedactionPino redaction (14 paths)Redacts password, token, apiKey, secret, authorization, cookie, sessionId, credentials, accessToken, refreshToken, bearerToken + auth/config paths
Env-Only Credentialsprocess.env + GitHub SecretsSAP_CLOUD_*, SAP_ONPREM_*, ANTHROPIC_API_KEY — never hardcoded; passed via CI secrets
Bridge IsolationWeakSet idempotencyensureBridgeInjected() prevents double injection and memory leaks
Input ValidationZod 4.3.6 schema validationAll external input validated at system boundaries before processing
.gitignore Secrets.env, .env.local, .auth/Secret files excluded from version control
SECURITY.mdResponsible disclosure policyResponsible disclosure via [email protected]

CI/CD Security Gates

GateTriggerEnforcement
Pre-commit Hookgit commitHusky: check-no-js-in-src + lint-staged (ESLint --fix, Prettier, markdownlint)
Commit Messagegit commitcommitlint @commitlint/config-conventional via commit-msg hook
Pre-push Hookgit pushHusky: typecheck + unit tests with coverage + build — all must pass
CI Security JobPush to main / PRnpm audit --audit-level=high --omit=dev — fails on HIGH/CRITICAL vulns
CodeQL ScanPush to main / PR / Weeklysecurity-extended queries; results in GitHub Security tab
CODEOWNERS ReviewEvery PR* @mrkanitkar — all files require owner review
Workflow PermissionsAll CI workflowsLeast-privilege: contents:read default, scoped write only where needed
Concurrency Controlci.ymlcancel-in-progress: true — prevents resource exhaustion from stacked runs
License HeadersEvery lint passeslint-plugin-headers enforces Apache 2.0 SPDX header as error on all src/ files

Best Practice Alignment

StandardPractices Adopted
PlaywrightWeb-first assertions, fixture DI, project dependencies for auth, test.step()
MicrosoftTSDoc standard, API Extractor, SDL security, OpenTelemetry, SHA-pinned Actions, cross-platform CI
Google TS StyleReadonly<> config, no barrel re-exports of internals, strict type checking
Google SREExponential backoff + jitter in retries, structured error codes
Google TestingTiered coverage (100% errors, 95% core, 90% global), per-file enforcement
Node.jsESM-first with CJS fallback, node: prefix, engines field, files field, dual exports
Anthropic / Clauderetryable + suggestions[] on errors, AI response envelope, checkpoint serialization
npmDual ESM+CJS via conditional exports, validated by attw, clean package.json files field

Documentation Strategy

LayerToolOutput
Inline DocumentationMicrosoft TSDoc with custom tags (@intent, @guarantee, @capability, @recipe, @ai, @aiContext, @sapModule, @businessContext)Every public function has TSDoc + @example
API Reference@microsoft/api-extractor + TypeDoc 0.28.17Generated API surface documentation
Lintingeslint-plugin-tsdoc (syntax: 'error') Zero tolerance on TSDoc syntax violations
WebsiteDocusaurus 3.x with custom React pagesArchitecture, features, personas, demo
Agent InstructionsCLAUDE.md, AGENTS.md, .cursor/rules/, .github/copilot-instructions.md7 IDE/agent configurations
Skill Files12 skill files in skills/playwright-praman-sap-testing/Agent-readable domain expertise

ESLint Plugin Coverage (11 Plugins)

Zero errors, zero warnings policy. All 11 plugins run on every lint pass.

typescript-eslint
Strict type-checked rulesv8.55.0
eslint-plugin-tsdoc
TSDoc syntax validationv0.5.0
eslint-plugin-playwright
Playwright best practicesv2.5.1
eslint-plugin-security
Security vulnerability detectionv3.0.1
@microsoft/eslint-plugin-sdl
SDL compliance rulesv1.1.0
eslint-plugin-sonarjs
Code smell + complexityv3.0.7
eslint-plugin-n
Node.js best practicesv17.24.0
eslint-plugin-promise
Promise anti-patternsv7.2.1
eslint-plugin-import-x
Import ordering + boundariesv4.16.1
eslint-plugin-unicorn
Modern JS best practicesv63.0.0
eslint-plugin-headers
Apache-2.0 license headersv1.3.4

Tiered Coverage Strategy

Google/Microsoft best practice: per-file enforcement ensures no single file hides behind project averages.

TierScopeStatementsBranchesFunctionsLines
Tier 1Error classes, public API (src/core/errors/)100%100%100%100%
Tier 2Core infrastructure (src/core/)95%90%95%95%
Tier 3All other modules (global)90%85%90%90%

Tool: @vitest/coverage-v8 with perFile: true. Reporters: text, lcov, json-summary, json, html. Watermarks: Yellow 80-95%, Green 95%+.

Azure Playwright Workspaces Integration

Run SAP E2E tests at scale with cloud-hosted browsers. GitHub Actions runs your test code, Azure runs the browsers, SAP Cloud is tested end-to-end — no local machine required.

G
GitHub Repo
Code Storage
  • Test scripts
  • Configs
  • Secrets
R
GitHub Runner
Test Execution
  • Node.js workers
  • playwright-praman
  • Fixtures & assertions
A
Azure Browsers
Browser Runtime
  • DOM rendering
  • page.evaluate()
  • UI5 bridge scripts
S
SAP System
App Under Test
  • Fiori Launchpad
  • OData services
  • Business data
GGitHub Repositorytests/, src/, configsRGitHub Actions RunnerNode.js + playwright-pramanAAzure Cloud BrowsersChromium / Firefox / WebKitSSAP Cloud SystemBTP / S/4HANA / Fiori1Trigger CI2Install & Build3Connect Browsers4Inject Bridge5Navigate to SAP6Execute Tests7Return Results8Upload Artifacts
Protocol:Git webhook
1Trigger CIGitHub Repository GitHub Actions Runner
Push, PR, schedule, or manual dispatch triggers GitHub Actions workflow

Acknowledgments

Praman was developed independently and does not contain code from the projects listed below. We would like to thank the following open-source project whose architectural patterns served as inspiration during Pramans design:

w5

The wdi5 project by the UI5 Community pioneered the approach of bridging Playwright (and WebdriverIO) with SAP UI5s control model in the browser. Architectural patterns for SAP UI5 browser bridge design were studied during Pramans independent, ground-up implementation. We are grateful to the wdi5 maintainers and contributors for their excellent open-source work under the Apache License 2.0.