# Praman Architecture & Design Decisions > Five-layer architecture, bridge internals, control proxy pattern, and ADRs. This file contains all documentation content in a single document following the llmstxt.org standard. ## Integrating with AI Agent Frameworks :::info Roadmap Native integrations with the frameworks below are **on the Praman roadmap**. The architecture and adapter contracts described on this page reflect the planned design. Community contributions and early feedback are welcome — open a discussion on [GitHub](https://github.com/mrkanitkar/playwright-praman/discussions). ::: Praman is designed from the ground up as an AI-first plugin. Its structured outputs — `AiResponse` envelopes, `IntentDescriptor` payloads, capability manifests, and vocabulary graphs — map naturally onto the tool-call and agent-step patterns used by modern AI orchestration frameworks. This page describes the planned integration model for five major frameworks, explains what each integration will look like in practice, and lists the capabilities each adapter will expose. --- ## Integration Architecture All framework adapters will follow the same two-layer model: ```text ┌──────────────────────────────────────────────────────────────┐ │ AI Agent Framework (LangGraph / AutoGen / OpenAI Agents…) │ │ │ │ ┌────────────────────────────────────────────────────────┐ │ │ │ Praman Adapter (playwright-praman/ai) │ │ │ │ │ │ │ │ Tool: navigateTo(intent) → ui5.press / page.goto │ │ │ │ Tool: fillField(id, val) → ui5.fill + waitForUI5 │ │ │ │ Tool: readControl(id) → ui5.getValue / getProperty│ │ │ │ Tool: waitForStable() → ui5.waitForUI5 │ │ │ │ Tool: getCapabilities() → CapabilityRegistry.list │ │ │ └────────────────────────────────────────────────────────┘ │ │ │ │ ┌────────────────────────────────────────────────────────┐ │ │ │ Playwright Browser ← Praman Fixtures + Bridge │ │ │ └────────────────────────────────────────────────────────┘ │ └──────────────────────────────────────────────────────────────┘ ``` Each adapter will expose Praman's SAP operations as **typed tool definitions** that the framework's orchestrator can call, chain, and retry. The browser state (Playwright `Page`) is kept alive across agent steps using the same fixture injection model Praman already uses. --- ## LangGraph [LangGraph](https://www.langchain.com/langgraph) (LangChain) models multi-step agent workflows as typed state machines where each node is a callable tool or sub-graph. Praman maps directly onto this model — each UI5 operation is a node with clearly typed inputs and outputs. ### Planned Capabilities | LangGraph Concept | Praman Mapping | | ------------------ | --------------------------------------------------------------- | | State node | One `test.step()` → one SAP UI interaction | | Tool node | `navigateTo`, `fillField`, `selectOption`, `assertControl` | | Conditional edge | Branch on `AiResponse.status` (`success` / `error` / `partial`) | | Interrupt + resume | Checkpoint serialization via Praman's `CheckpointState` type | | Human-in-the-loop | Pause on SAP validation error, surface to operator, resume | ### What the Adapter Will Provide ```typescript // Planned API — not yet available const tools = createLangGraphAdapter(page, ui5); // tools exposes: navigateTo, fillField, readControl, // waitForStable, openValueHelp, selectFromTable, // assertFieldValue, getCapabilities ``` ### Example Agent Flow (Planned) ```typescript const graph = new StateGraph({ channels: { messages: { value: [] } } }) .addNode('navigate', tools.navigateTo) .addNode('openDialog', tools.press) .addNode('fillMaterial', tools.fillField) .addNode('fillPlant', tools.fillField) .addNode('submit', tools.press) .addConditionalEdges('submit', (state) => state.lastResult.status === 'success' ? 'verify' : 'handleError', ) .addNode('verify', tools.assertControl) .addNode('handleError', tools.gracefulCancel) .compile(); ``` :::tip Why LangGraph fits SAP workflows SAP processes are inherently stateful and branching — a BOM creation may succeed, fail with a validation error, or require a different plant/material combination. LangGraph's conditional edges and interrupt/resume model handles this naturally without hard-coded `if/else` chains in test scripts. ::: --- ## AutoGen (Microsoft) [AutoGen](https://microsoft.github.io/autogen/) is Microsoft's open-source framework for building multi-agent systems where specialized agents collaborate to complete complex tasks. Its conversational agent model aligns with Praman's planner → generator → healer pipeline. ### Planned Capabilities | AutoGen Concept | Praman Mapping | | ----------------- | ---------------------------------------------------------- | | `AssistantAgent` | Praman planner — discovers SAP UI, produces test plan | | `UserProxyAgent` | Praman executor — runs Playwright actions, reports results | | `GroupChat` | Planner + Generator + Healer collaborate on a test suite | | Tool registration | SAP UI tools registered on the executor agent | | Code execution | Generated `.spec.ts` runs via `npx playwright test` | ### What the Adapter Will Provide ```typescript // Planned API — not yet available const sapTools = createAutoGenTools(page, ui5); // Register on your AutoGen UserProxyAgent's function map ``` ### Planned Agent Topology ```text ┌─────────────────┐ instructions ┌──────────────────┐ │ PlannerAgent │ ──────────────────► │ ExecutorAgent │ │ (AssistantAgent│ test steps │ (UserProxyAgent) │ │ + SAP skills) │ │ + Praman tools │ └─────────────────┘ └────────┬─────────┘ ▲ │ │ result + errors │ └──────────────────────────────────────────┘ HealerAgent joins on failure ``` --- ## Microsoft Agent Framework (Semantic Kernel) [Semantic Kernel](https://learn.microsoft.com/en-us/semantic-kernel/overview/) is Microsoft's SDK for building AI-powered applications with plugins, planners, and memory. Praman's capability registry and vocabulary system are designed to expose as Semantic Kernel plugins. ### Planned Capabilities | Semantic Kernel Concept | Praman Mapping | | ----------------------- | ----------------------------------------------------------------------- | | Native plugin | `SapUi5Plugin` — exposes all `ui5.*` fixtures as SK functions | | Planner | Auto-plans multi-step SAP workflows from a natural language goal | | Memory | Stores discovered control maps between agent sessions | | Function calling | Each Praman tool becomes a describable SK kernel function | | Filters | Pre/post hooks map to Praman's `BeforeAction` / `AfterAction` lifecycle | ### What the Adapter Will Provide ```typescript // Planned API — not yet available const kernel = new Kernel(); kernel.addPlugin(new SapUi5Plugin(page, ui5), 'SapUI5'); // Kernel can now plan: "Fill the BOM creation form and submit" // and call SapUI5.fillField, SapUI5.press, SapUI5.waitForStable ``` ### Plugin Functions (Planned) | Function | Description | | ----------------------------- | ----------------------------------- | | `navigateToApp(appName)` | FLP navigation to a named Fiori app | | `fillField(controlId, value)` | `ui5.fill()` with SAP field binding | | `pressButton(controlId)` | `ui5.press()` with stability wait | | `readFieldValue(controlId)` | `ui5.getValue()` | | `openValueHelp(controlId)` | Open and enumerate value help table | | `selectFromValueHelp(index)` | Pick row by OData binding index | | `waitForStableUI()` | `ui5.waitForUI5()` | | `getAvailableApps()` | Queries FLP tile catalog | --- ## OpenAI Agents SDK The [OpenAI Agents SDK](https://openai.github.io/openai-agents-python/) (previously Swarm) provides a lightweight framework for building agents that hand off between each other and call registered tools. Praman tools map directly onto the SDK's function-tool model. ### Planned Capabilities | OpenAI Agents SDK Concept | Praman Mapping | | ------------------------- | --------------------------------------------------------------------- | | `Agent` | One agent per Praman role (planner, executor, healer) | | `function_tool` | Every `ui5.*` fixture becomes a callable tool definition | | Handoffs | Planner → Generator → Healer pipeline with structured handoff context | | Structured output | `AiResponse` maps to Pydantic / Zod response schemas | | Tracing | Playwright `test.info().annotations` surface in OpenAI trace UI | ### What the Adapter Will Provide ```typescript // Planned API — not yet available const tools = createOpenAITools(page, ui5); // tools: array of OpenAI function_tool definitions // ready to pass to new Agent({ tools }) ``` ### Planned Tool Schema Example ```json { "name": "fill_sap_field", "description": "Fill a SAP UI5 input field and fire the change event. Use for SmartField, MDC Field, and sap.m.Input controls.", "parameters": { "type": "object", "properties": { "controlId": { "type": "string", "description": "Stable SAP UI5 control ID from discovery" }, "value": { "type": "string", "description": "Value to set" }, "searchOpenDialogs": { "type": "boolean", "default": true } }, "required": ["controlId", "value"] } } ``` --- ## Google Agent Development Kit (ADK) [Google's Agent Development Kit](https://google.github.io/adk-docs/) (ADK) is Google's open-source framework for building and deploying multi-agent systems, with first-class support for tool use, multi-agent pipelines, and Vertex AI integration. The ADK's `BaseTool` interface is a natural fit for Praman's typed SAP tools. ### Planned Capabilities | Google ADK Concept | Praman Mapping | | ------------------ | ---------------------------------------------------------- | | `BaseTool` | Each `ui5.*` fixture becomes an ADK tool with typed schema | | `LlmAgent` | SAP planner, generator, and healer as ADK agents | | `SequentialAgent` | Plan → Generate → Heal pipeline | | `ParallelAgent` | Concurrent discovery of multiple Fiori apps | | Vertex AI backend | Cloud-hosted planning with Gemini models | | Session state | Shared control map and vocabulary across agent steps | ### What the Adapter Will Provide ```typescript // Planned API — not yet available const sapTools = createADKTools(page, ui5); const plannerAgent = new LlmAgent({ name: 'SapTestPlanner', model: 'gemini-2.0-flash', tools: sapTools, instruction: 'Discover the SAP UI5 app and produce a structured test plan.', }); ``` ### ADK Pipeline (Planned) ```text SequentialAgent ├── SapPlannerAgent → discovers UI5 controls, writes test plan ├── SapGeneratorAgent → converts plan to playwright-praman spec └── SapHealerAgent → runs test, reads failure, iterates ``` :::tip Vertex AI + SAP The ADK integration will support Vertex AI backends, enabling teams to run the planning and generation pipeline entirely within Google Cloud — useful for enterprises with data residency requirements. ::: --- ## Comparison | Framework | Best For | Orchestration Model | Praman Fit | | --------------------- | -------------------------------------------------- | -------------------------- | -------------------------------------- | | **LangGraph** | Complex branching SAP workflows, human-in-the-loop | State machine graph | Excellent — maps to test.step() | | **AutoGen** | Multi-agent collaboration (planner + healer) | Conversational agents | Excellent — mirrors Praman pipeline | | **Semantic Kernel** | Microsoft ecosystem, enterprise .NET/Python | Plugin + planner | Good — capability registry as plugin | | **OpenAI Agents SDK** | Lightweight, fast iteration, OpenAI models | Function tools + handoffs | Good — clean tool schema mapping | | **Google ADK** | Vertex AI, Gemini models, GCP-native pipelines | Sequential/parallel agents | Good — structured ADK tool definitions | --- ## Playwright CLI Integration In addition to the framework adapters above, Praman supports a **Playwright CLI** integration path. The CLI is a token-efficient alternative to MCP where agents invoke `npx playwright` commands directly instead of connecting through a persistent server. | Integration | Connection Model | Token Efficiency | Best For | | ---------------------- | -------------------- | ---------------- | -------------------------------------------- | | **MCP Server** | Persistent WebSocket | Moderate | Real-time agent loops, VS Code extensions | | **Playwright CLI** | Stateless commands | High | CI/CD pipelines, token-constrained agents | | **Framework Adapters** | Native SDK tools | Varies | Multi-agent orchestration, complex workflows | The CLI works with any agent framework that can execute shell commands — including LangGraph, AutoGen, and OpenAI Agents SDK — without requiring a native adapter. See the [CLI Agents Guide](./playwright-cli-agents.md) for building agent loops with the CLI, and [MCP vs CLI](./mcp-vs-cli.md) for a detailed comparison. --- ## Current Workaround: Use Praman Agents Directly While framework-native adapters are in development, you can orchestrate Praman's three agents today using **Claude Code MCP**, **Playwright CLI**, or **GitHub Copilot Agent Mode** with the included agent definitions: ```text .github/agents/praman-sap-planner.agent.md .github/agents/praman-sap-generator.agent.md .github/agents/praman-sap-healer.agent.md ``` See [Running Your Agent for the First Time](./running-your-agent.md) for the complete pipeline. --- ## Contribute or Follow Progress - **GitHub Discussions**: Share your framework preference and use case - **Issues**: Track individual adapter issues under the `ai-adapters` label - **Roadmap**: The order of delivery will reflect community demand — upvote your framework ```text Planned delivery order (subject to change): 1. OpenAI Agents SDK adapter ← lightest surface area 2. LangGraph adapter ← most requested for SAP workflows 3. Google ADK adapter ← Vertex AI enterprise demand 4. AutoGen adapter ← multi-agent collaboration 5. Semantic Kernel plugin ← Microsoft ecosystem ``` --- ## AI Integration Praman is an AI-first test automation plugin. The `playwright-praman/ai` sub-path provides structured interfaces for LLM-driven test generation, page analysis, and autonomous agent workflows. ## AiResponse Envelope All AI operations return an `AiResponse` discriminated union, providing type-safe handling without casting: ```typescript type AiResponse = | { status: 'success'; data: T; metadata: AiResponseMetadata } | { status: 'error'; data: undefined; error: AiResponseError; metadata: AiResponseMetadata } | { status: 'partial'; data: Partial; error?: AiResponseError; metadata: AiResponseMetadata }; ``` Narrow on `status` to get type-safe access: ```typescript function handle(response: AiResponse): T | undefined { if (response.status === 'success') { return response.data; // TypeScript knows this is T } if (response.status === 'error') { console.error(response.error.code, response.error.message); if (response.metadata.retryable) { // Agent can retry this operation } } return undefined; } ``` ### AiResponseMetadata Every response carries metadata for performance tracking and self-healing: ```typescript interface AiResponseMetadata { readonly duration: number; // Elapsed time in ms readonly retryable: boolean; // Can the caller retry? readonly suggestions: string[]; // Recovery hints for agents/testers readonly model?: string; // Model identifier (e.g., 'gpt-4o') readonly tokens?: number; // Total tokens consumed } ``` ## Provider Abstraction Praman supports three LLM providers through a unified `LlmService` interface: | Provider | SDK | Config Key | | ---------------- | ------------------- | -------------------------- | | Azure OpenAI | `openai` | `provider: 'azure-openai'` | | OpenAI | `openai` | `provider: 'openai'` | | Anthropic Claude | `@anthropic-ai/sdk` | `provider: 'anthropic'` | Provider SDKs are loaded via dynamic `import()` and remain optional dependencies. If the SDK is not installed, a clear `AIError` with installation instructions is thrown. ### Configuration ```typescript // praman.config.ts export default { ai: { provider: 'azure-openai', model: 'gpt-4o', endpoint: 'https://my-resource.openai.azure.com/', apiKey: process.env.AZURE_OPENAI_API_KEY, temperature: 0.2, maxTokens: 4096, apiVersion: '2024-02-15-preview', deployment: 'my-gpt4o-deployment', }, }; ``` ### Creating an LLM Service ```typescript const llm = createLlmService(config); // complete() takes a single string prompt and an optional Zod schema: const response = await llm.complete('Generate a test for the login page', schema); // For multi-turn conversations, use chat() with a messages array: const chatResponse = await llm.chat( [ { role: 'system', content: 'You are a test generator...' }, { role: 'user', content: 'Generate a test for the login page' }, ], schema, ); ``` ## AgenticCheckpoint For long-running multi-step AI workflows, Praman provides checkpoint serialization. The `AgenticCheckpoint` allows AI agents to resume from the last successful step on failure: ```typescript const checkpoint: AgenticCheckpoint = { sessionId: 'sess-001', currentStep: 2, completedSteps: ['discover', 'plan'], remainingSteps: ['generate', 'validate'], state: { pageUrl: 'https://my.app/launchpad' }, timestamp: new Date().toISOString(), }; ``` The `AgenticHandler` serializes progress automatically: ```typescript // Constructor: new AgenticHandler(llm, contextBuilder, capabilityRegistry, recipeRegistry?) // - llm: an LlmService instance // - contextBuilder: a function that builds page context for LLM prompts // - capabilityRegistry: the CapabilityRegistry instance // - recipeRegistry: optional RecipeRegistry instance const handler = new AgenticHandler(llmService, contextBuilder, capabilityRegistry, recipeRegistry); // generateTest returns AiResponse with checkpoint data const result = await handler.generateTest('Test the purchase order creation flow'); ``` ## Page Context and toAIContext() ### buildPageContext() Discovers all UI5 controls on the page and builds a structured context for LLM prompts: ```typescript const context = await buildPageContext(page, config); if (context.status === 'success') { const { controls, formFields, buttons, tables } = context.data; // Feed to LLM as structured context } ``` The returned `PageContext` partitions controls by semantic category: ```typescript interface PageContext { readonly url: string; readonly ui5Version?: string; readonly controls: DiscoveredControl[]; // All controls readonly formFields: DiscoveredControl[]; // Inputs, selects, etc. readonly buttons: DiscoveredControl[]; // Buttons, clickable triggers readonly tables: DiscoveredControl[]; // Data tables and lists readonly navigationElements: DiscoveredControl[]; readonly timestamp: string; } ``` ### toAIContext() on Errors Every `PramanError` provides `toAIContext()` for machine-readable error introspection: ```typescript try { await ui5.click({ id: 'submitBtn' }); } catch (error) { if (error instanceof PramanError) { const context = error.toAIContext(); // { // code: 'ERR_CONTROL_NOT_FOUND', // message: 'Control not found: submitBtn', // attempted: 'Find control with selector: {"id":"submitBtn"}', // retryable: true, // suggestions: ['Verify the control ID exists in the UI5 view', ...] // } } } ``` ## Token-Aware Metadata All AI responses include token usage information when the provider reports it: ```typescript const result = await handler.generateTest('Create a PO test'); if (result.status === 'success') { console.log(result.data.metadata.tokens); // { input: 1200, output: 340 } console.log(result.data.metadata.model); // 'gpt-4o' console.log(result.data.metadata.duration); // 1850 (ms) } ``` This enables cost tracking and prompt optimization in CI pipelines. ## Capability Registry The `CapabilityRegistry` exposes Praman's API surface to AI agents for test generation: ```typescript const registry = new CapabilityRegistry(); // Query by category (CapabilityCategory enum value) const tableCaps = registry.byCategory('table'); // Get all capabilities for AI prompt context const aiContext = registry.forAI(); // Provider-specific formatting const claudeFormat = registry.forProvider('claude'); // XML-structured const openaiFormat = registry.forProvider('openai'); // JSON registry snapshot ``` Each capability entry includes: ```typescript interface CapabilityEntry { readonly id: string; // 'UI5-UI5-001' (UI5-PREFIX-NNN format) readonly qualifiedName: string; // 'ui5.clickButton' readonly name: string; // 'clickButton' readonly description: string; // 'Clicks a UI5 button by selector' readonly category: CapabilityCategory; // 'ui5' (enum value) readonly priority: 'fixture' | 'namespace' | 'implementation'; // REQUIRED readonly usageExample: string; // "await ui5.click({ id: 'submitBtn' })" readonly registryVersion: 1; // Literal 1 for schema versioning readonly intent?: string; // Optional intent tag readonly sapModule?: string; // Optional SAP module tag readonly controlTypes?: string[]; // UI5 control types readonly async?: boolean; // Whether this is async } ``` Capabilities are auto-generated from TSDoc annotations via `npm run generate:capabilities`. The `registryVersion` literal enables AI agents to detect stale cached data. ## Recipe Registry The `RecipeRegistry` provides curated test patterns that AI agents can emit verbatim or adapt: ```typescript const registry = new RecipeRegistry(); // Filter by domain and priority const authRecipes = registry.select({ domain: 'auth', priority: 'essential' }); // Get the top N essential recipes for prompt context const topFive = registry.getTopRecipes(5); // Get all recipes formatted for AI consumption const aiRecipes = registry.forAI(); ``` Each recipe entry: ```typescript interface RecipeEntry { readonly id: string; // 'recipe-ui5-button-click' (recipe-kebab-case) readonly name: string; // 'Button Click' readonly description: string; // What this recipe demonstrates readonly domain: string; // 'ui5' (domain grouping) readonly priority: 'essential' | 'recommended' | 'optional' | 'advanced' | 'deprecated'; readonly capabilities: string[]; // ['UI5-UI5-003'] (capability IDs used) readonly pattern: string; // Ready-to-use TypeScript code pattern } ``` ## AI Surface Modes Praman supports two AI modes (Decision D9): **Mode A -- SKILL.md for code-gen agents**: A strengthened skill file with typed API surface, capability/recipe registries, and usage examples. Agents like GitHub Copilot, Claude Code, and Cursor consume this to generate test code. **Mode C -- Agentic fixture for in-test agents**: The `AgenticHandler` fixture with Zod-validated LLM output for autonomous test generation during test execution. The handler builds page context, generates test code, and validates the output against schemas. There is no MCP server -- Praman remains a Playwright plugin and does not duplicate browser management. --- ## Architecture Overview Praman v1.0 ships as a single npm package (`playwright-praman`) with sub-path exports, organized into a strict 6-layer architecture where lower layers never import from higher layers. ## 6-Layer Architecture ```text ┌─────────────────────────────────────────────────────────────────────────┐ │ Test Author / AI Agent │ │ import { test, expect } from 'playwright-praman' │ │ import { procurement } from 'playwright-praman/intents' │ └──────────────────────────────────┬──────────────────────────────────────┘ │ ┌──────────────────────────────────▼──────────────────────────────────────┐ │ Layer 5 — Public API (6 sub-path exports) │ │ │ │ playwright-praman /ai /intents /vocabulary /fe /reporters │ └──────────────────────────────────┬──────────────────────────────────────┘ │ ┌──────────────────────────────────▼──────────────────────────────────────┐ │ Layer 4 — Fixtures + Modules │ │ │ │ Fixtures core · auth · nav · stability · ai · fe · intent · flp │ │ Merged via mergeTests() into a single test object │ │ Modules table · dialog · date · navigation · odata · workzone │ │ Auth cloud-saml · office365 · onprem · api-key · cert · tenant │ └──────────────────────────────────┬──────────────────────────────────────┘ │ ┌──────────────────────────────────▼──────────────────────────────────────┐ │ Layer 3 — Control Proxy │ │ │ │ control-proxy.ts · discovery.ts · ui5-object.ts · cache.ts │ │ 3-tier discovery: object cache → stable ID → type + property search │ └──────────────────────────────────┬──────────────────────────────────────┘ │ ┌──────────────────────────────────▼──────────────────────────────────────┐ │ Layer 2 — Bridge │ │ │ │ injection.ts (lazy bridge bootstrap into browser context) │ │ browser-scripts/ inject-ui5 · find-control · execute-method · ... │ │ Interaction strategies: UI5Native · DomFirst · OPA5 │ └──────────────────────────────────┬──────────────────────────────────────┘ │ ┌──────────────────────────────────▼──────────────────────────────────────┐ │ Layer 1 — Core Infrastructure │ │ │ │ config (Zod) · errors (14 subclasses) · logging (pino) │ │ telemetry (OTel — real when enabled) · types · utils · compat │ └──────────────────────────────────┬──────────────────────────────────────┘ │ ┌──────────────────────────────────▼──────────────────────────────────────┐ │ @playwright/test (external dependency) │ │ page · browser · context · expect │ └─────────────────────────────────────────────────────────────────────────┘ ``` ## Layer Dependency Rules The architecture enforces a strict **downward-only dependency rule**: - Layer 1 (Core) has zero internal dependencies beyond Node.js builtins. - Layer 2 (Bridge) imports only from Layer 1. - Layer 3 (Proxy) imports from Layers 1 and 2. - Layer 4 (Fixtures + Modules) imports from Layers 1, 2, and 3. - Layer 5 (Public API) re-exports from Layers 1–4; no higher layer imports from it. This is enforced at compile time via TypeScript path aliases (`#core/*`, `#bridge/*`, `#proxy/*`, `#fixtures/*`) and ESLint import rules. :::info Internal path aliases The aliases below are internal to Praman's source code and are not available to package consumers. They enforce layer boundaries during development. ::: ```typescript // Allowed: Layer 3 importing from Layer 1 // Allowed: Layer 3 importing from Layer 2 // FORBIDDEN: Layer 1 importing from Layer 3 — compile error // import { ControlProxy } from '#proxy/control-proxy.js'; ``` ## Sub-Path Exports Praman ships 6 sub-path exports, each targeting a specific concern: | Sub-Path Export | Purpose | Key Modules | | ------------------------------ | --------------------------- | ---------------------------------------------------- | | `playwright-praman` | Core fixtures, expect, test | Fixtures, selectors, matchers | | `playwright-praman/ai` | AI agent integration | CapabilityRegistry, RecipeRegistry, AgenticHandler | | `playwright-praman/intents` | SAP domain intent APIs | Core wrappers, 5 domain namespaces | | `playwright-praman/vocabulary` | Business term resolution | VocabularyService, fuzzy matcher | | `playwright-praman/fe` | Fiori Elements helpers | ListReport, ObjectPage, FE tables | | `playwright-praman/reporters` | Custom Playwright reporters | ComplianceReporter, ODataTraceReporter, OTelReporter | Each export provides both ESM and CJS outputs: ```json { "./ai": { "types": { "import": "./dist/ai/index.d.ts", "require": "./dist/ai/index.d.cts" }, "import": "./dist/ai/index.js", "require": "./dist/ai/index.cjs" } } ``` All exports are validated by `@arethetypeswrong/cli` (attw) in CI. ## Design Decisions Summary (D1–D29) The architecture is guided by 29 documented design decisions: | # | Decision | Category | | --- | ------------------------------------------------------------------------ | ------------- | | D1 | Single package with sub-path exports (not monorepo) | Packaging | | D2 | Internal fixture composition via `extend()` chain | Fixtures | | D3 | ~~Version-negotiated bridge adapters~~ (removed — superseded by D4) | Bridge | | D4 | Hybrid typed proxy: 199 typed interfaces + dynamic fallback | Proxy | | D5 | 4-layer observability: Reporter + pino + OTel + AI telemetry | Observability | | D6 | Zod validation at external boundaries only | Validation | | D7 | Zod-validated `praman.config.ts` with env overrides | Config | | D8 | Unified `PramanError` hierarchy with 13 subclasses | Errors | | D9 | AI Mode A (SKILL.md) + Mode C (agentic fixture) | AI | | D10 | Vitest unit tests + Playwright integration tests | Testing | | D11 | No plugin API in v1.0 | Extension | | D12 | Auto-generated SKILL.md + Docusaurus + TypeDoc | Docs | | D13 | Apache 2.0 license | Legal | | D14 | Playwright `>=1.57.0 <2.0.0` with CI matrix | Compat | | D15 | Security: dep scanning, secret redaction, SBOM, provenance | Security | | D16 | Single unified proxy (no double-proxy) | Proxy | | D17 | Bidirectional proxy conversion (`UI5Object` <-> `UI5ControlProxy`) | Proxy | | D18 | Integrated discovery factory (removed dead code) | Discovery | | D19 | Centralized `__praman_getById()` (3-tier API resolution) | Bridge | | D20 | Browser objectMap with TTL + cleanup | Memory | | D21 | Shared interaction logic across strategies | Interactions | | D22 | Auto-generated method signatures (199 interfaces, 4,092 methods) | Types | | D23 | `skipStabilityWait` as global config + per-selector override | Stability | | D24 | `exec()` with `new Function()` + security documentation | Security | | D25 | Visibility preference default (prefer visible controls) | Discovery | | D26 | UI5Object AI introspection as first-class capability | AI | | D27 | Module size <= 300 LOC guideline (with documented exceptions) | Quality | | D28 | Auth via Playwright project dependencies (not globalSetup) | Auth | | D29 | Enhanced error model + AI response envelope | AI/Errors | ## Module Decomposition Rationale The predecessor plugin (v2.5.0) suffered from monolithic files: | File | LOC | Problem | | ---------------------- | ----- | ---------------------------------------- | | `ui5-handler.ts` | 2,318 | God object handling all UI5 operations | | `plugin-fixtures.ts` | 2,263 | All 17 fixtures in one file | | `ui5-control-proxy.ts` | 1,829 | Double-proxy with redundant interception | Praman v1.0 decomposes these into focused modules following SRP (Single Responsibility Principle): **Fixtures** are split by domain: - `core-fixtures.ts` — UI5 control operations - `auth-fixtures.ts` — Authentication strategies - `nav-fixtures.ts` — Navigation and routing - `stability-fixtures.ts` — Wait conditions and stability checks - `ai-fixtures.ts` — AI agent fixtures - `fe-fixtures.ts` — Fiori Elements fixtures - `intent-fixtures.ts` — Intent API fixtures - `shell-footer-fixtures.ts`, `flp-locks-fixtures.ts`, `flp-settings-fixtures.ts` — FLP-specific fixtures **Modules** handle domain operations independently of fixtures: - `table.ts` + `table-operations.ts` + `table-filter-sort.ts` — Grid and SmartTable - `dialog.ts` — Dialog detection and interaction - `date.ts` — DatePicker and range fields - `navigation.ts` — UI5 routing and hash navigation - `odata.ts` + `odata-http.ts` — OData model access and CRUD - `workzone.ts` — BTP WorkZone helpers **UI5Handler** was decomposed from a 2,318 LOC god object into: - `ui5-handler.ts` (588 LOC) — 16 core methods - `shell-handler.ts` (102 LOC) — FLP shell operations - `footer-handler.ts` (119 LOC) — Footer bar operations - `navigation.ts` (201 LOC) — Route and hash navigation **Control proxy** was simplified from a double-proxy pattern (1,829 LOC) into: - `control-proxy.ts` (653 LOC) — Unified single proxy handler - `ui5-object.ts` (383 LOC) — Non-control object proxy - `discovery.ts` (111 LOC) — 3-tier control discovery - `cache.ts` (104 LOC) — Proxy cache with RegExp keys Every module targets <= 300 LOC. Exceptions are documented with justification comments. ## Codebase Metrics | Metric | Value | | ------------------------ | --------------------------- | | Source files | ~150 | | Source LOC | ~29,000 | | Public functions | 208 | | Test files | 109 | | Unit tests | ~2,000 passing | | Typed control interfaces | 199 (4,092 methods) | | Error subclasses | 13 | | Coverage | >98% (per-file enforcement) | | Lint errors | 0 | | Type errors | 0 | | ESM + CJS dual build | Validated by attw | --- ## Bridge Internals :::info[Contributor Only] This page documents Praman's internal bridge architecture. The APIs shown here use internal path aliases (`#bridge/*`) and are not accessible to end users of the `playwright-praman` package. This content is intended for contributors and plugin developers. ::: The bridge is the communication layer between Praman's Node.js test code and the SAP UI5 framework running in the browser. Understanding its internals is essential for debugging and extending Praman. ## Bridge Injection via page.evaluate() Praman injects a bridge namespace (`window.__praman_bridge`) into the browser context using Playwright's `page.evaluate()`. The bridge provides: - UI5 module references (RecordReplay, Element, Log) - Version detection (`sap.ui.version`) - Object map for non-control storage (UUID-keyed) - `getById()` with 3-tier API resolution (Decision D19) - Helper functions: `isPrimitive`, `saveObject`, `getObject`, `deleteObject` ### Injection Modes Praman supports two injection strategies: **Lazy injection** (default): Triggered on first UI5 operation via `ensureBridgeInjected()`. ```typescript // Every proxy and handler method calls this before browser operations await ensureBridgeInjected(page); ``` **Eager injection**: Registered via `addInitScript()` before any page loads. Useful for auth flows where the bridge must be ready before the first navigation. ```typescript // Inject before any navigation (recommended for auth flows) await injectBridgeEager(page); await page.goto('https://sap-system.example.com/app'); // Bridge is already available when UI5 loads ``` ### Injection Lifecycle 1. Wait for UI5 framework availability (`sap.ui.require` exists) 2. Execute bridge injection script (creates `window.__praman_bridge`) 3. Wait for bridge readiness (`window.__praman_ready === true`) Injection tracking uses `WeakSet` to avoid memory leaks. After page navigation invalidates the bridge, call `resetPageInjection(page)` so the next operation re-injects. ```typescript // After navigation invalidates the bridge resetPageInjection(page); // Next ensureBridgeInjected() call will re-inject ``` ## Serialization Constraints **This is the single most critical concept for anyone working on bridge code.** `page.evaluate()` serializes ONLY the function body via `fn.toString()`. This has profound implications: - Module-level functions are NOT included in the serialized output. - Imports and closures are NOT available inside `page.evaluate()`. - TypeScript types (imports, `as` casts, type aliases) are fine -- they are erased at compile time. ### The Inner Function Rule ALL helper functions MUST be declared as inner function declarations inside the evaluated function: ```typescript // CORRECT: helper is an inner function await page.evaluate(() => { function getControlById(id: string) { // This function IS available in the browser context return sap.ui.getCore().byId(id); } return getControlById('myButton'); }); // WRONG: helper is a module-level function function getControlById(id: string) { return sap.ui.getCore().byId(id); } await page.evaluate(() => { // ReferenceError: getControlById is not defined return getControlById('myButton'); }); ``` ### Why Unit Tests Give False Positives Unit tests run in Node.js where module-level functions ARE accessible via closure. This means a unit test will pass even when the code would fail in the browser: ```typescript // This module-level function is accessible in Node.js tests... function helperFn() { return 'works'; } // ...so this test PASSES in Vitest but the code FAILS in the browser it('should work', () => { const fn = () => helperFn(); expect(fn()).toBe('works'); // PASSES in Node.js }); // In the browser, page.evaluate(() => helperFn()) throws ReferenceError ``` If you see `sonarjs/no-identical-functions` warnings about duplicate inner functions, suppress them with an ESLint disable comment. The duplication is intentional and required. ### String-Based Scripts For complex browser operations, Praman uses string-based scripts instead of arrow functions. This avoids serialization issues entirely: ```typescript // Bridge injection uses string evaluation const script = createBridgeInjectionScript(); // returns a string await page.evaluate(script); // Find-control and execute-method also use strings const findScript = createFindControlScript(selector); const result = await page.evaluate(findScript); ``` ## 3-Tier API Resolution (D19) The bridge provides a centralized `getById()` function that resolves UI5 control IDs using three fallback tiers: ```text Tier 1: Element.getElementById() (UI5 1.108+) Tier 2: ElementRegistry.get() (UI5 1.84+) Tier 3: sap.ui.getCore().byId() (All versions) ``` This is registered once during injection. All browser scripts call `bridge.getById()` instead of duplicating the lookup logic (which was a problem in dhikraft v2.5.0 where 6 files had independent copies). ## Frame Navigation (WorkZone Dual-Frame) SAP BTP WorkZone (formerly Fiori Launchpad as a Service) renders applications inside nested iframes. Praman handles this transparently: ```text +----------------------------------+ | Shell Frame (WorkZone chrome) | | +----------------------------+ | | | App Frame (Fiori app) | | | | UI5 controls live here | | | +----------------------------+ | +----------------------------------+ ``` The bridge must be injected into the **app frame**, not the shell frame. Praman's fixture layer automatically detects the WorkZone dual-frame layout and targets the correct frame for bridge injection and control operations. After frame navigation (e.g., navigating between Fiori Launchpad tiles), the bridge state in the previous frame is invalidated. The injection tracking resets and re-injects on the next operation. ## Object Map Lifecycle Non-control UI5 objects (Models, BindingContexts, Routers, etc.) cannot be serialized across the `page.evaluate()` boundary. Praman stores them in a browser-side Map keyed by UUID: ```typescript // Browser side: save an object reference const uuid = bridge.saveObject(myModel, 'model'); // Returns: 'a1b2c3d4-...' // Node side: reference the object by UUID in subsequent calls await page.evaluate((uuid) => bridge.getObject(uuid).getProperty('/Name'), uuid); ``` ### TTL-Based Cleanup (D20) To prevent memory leaks in long test runs, objects are stored with timestamps: ```javascript bridge.objectMap.set(uuid, { value: obj, type: type || 'unknown', storedAt: Date.now(), }); ``` A cleanup function evicts entries older than the configured TTL. This pairs with the Node-side `UI5ObjectCache` (TTL + LRU eviction) to keep both sides in sync. ## Stability Checks Before performing control operations, Praman waits for UI5 to reach a stable (idle) state: ```typescript // waitForUI5Stable checks these conditions: // 1. No pending HTTP requests // 2. No pending timeouts // 3. No pending UI5 rendering updates // 4. RecordReplay is available and idle await page.waitForFunction(waitForUI5StableScript, { timeout }); ``` The `skipStabilityWait` option (D23) can be configured globally or per-selector for pages with third-party overlays (like WalkMe) that interfere with stability detection. ## Browser Scripts The `src/bridge/browser-scripts/` directory contains the scripts executed in the browser context: | Script | Purpose | | -------------------------- | ---------------------------------------------------------------- | | `inject-ui5.ts` | Core bridge initialization, module references, version detection | | `find-control.ts` | UI5 control discovery by selector | | `execute-method.ts` | Remote method invocation on UI5 controls | | `get-selector.ts` | Reverse lookup: DOM element to UI5 selector | | `get-version.ts` | UI5 version detection | | `inspect-control.ts` | Control metadata introspection for AI | | `object-map.ts` | UUID-keyed non-control object storage with TTL | | `find-control-matchers.ts` | Property matcher generation for selectors | ## Interaction Strategies Praman provides three strategies for interacting with UI5 controls: | Strategy | Approach | Best For | | ------------------- | ---------------------------------------- | -------------------------------------------------- | | `UI5NativeStrategy` | `fire*` events -> `fireTap` -> DOM click | Standard UI5 controls, most reliable | | `DomFirstStrategy` | DOM click + auto-detect input type | Controls with custom renderers | | `Opa5Strategy` | `RecordReplay.interactWithControl` | Complex controls needing SAP's own interaction API | The strategy factory selects the appropriate strategy based on configuration and control type. --- ## Control Proxy Pattern Every UI5 control discovered by Praman is wrapped in a JavaScript `Proxy` that routes method calls through Playwright's `page.evaluate()` to the real browser-side control. This page explains how the proxy works, what it can do, and how it compares to wdi5. ## How createControlProxy() Works When you call `ui5.control()`, Praman: 1. Discovers the control in the browser (via the multi-strategy chain) 2. Stores the control's ID as a reference 3. Returns a JavaScript `Proxy` object that intercepts all property access ```typescript const button = await ui5.control({ id: 'saveBtn' }); // `button` is a Proxy, not the actual UI5 control ``` :::tip Typed Returns When you provide `controlType` as a string literal, the proxy is typed to the specific control interface — e.g., `UI5Button` instead of `UI5ControlBase`. See [Typed Control Returns](/docs/guides/typed-controls). ::: When you call a method on the proxy (e.g., `button.getText()`), the proxy's `get` trap: 1. Checks if the method is a **built-in safe method** (press, enterText, select, etc.) 2. Checks if the method is **blacklisted** (lifecycle/internal methods) 3. If neither, forwards the call to the browser via `page.evaluate()` ```text Node.js Browser ─────── ─────── button.getText() → Proxy get trap → method forwarder → page.evaluate(fn, controlId) → sap.ui.getCore().byId(controlId) → control.getText() → return 'Save' ← result: 'Save' ``` ## The 7-Type Return System When a method executes in the browser, the result is classified into one of 7 types: | Return Type | Example | Proxy Behavior | | ----------------------- | ------------------------------------- | --------------------------- | | **Primitive** | `string`, `number`, `boolean`, `null` | Returned directly | | **Plain object** | `{ key: 'val' }` | Returned as-is (serialized) | | **Array of primitives** | `['a', 'b']` | Returned as-is | | **UI5 element** | Single control reference | Wrapped in a new proxy | | **Array of elements** | Aggregation result | Array of new proxies | | **undefined** | Setter return / void | Returns `undefined` | | **Non-serializable** | Circular refs, DOM nodes | Throws `ControlError` | This means you can chain proxy calls to navigate the control tree: ```typescript const table = await ui5.control({ id: 'poTable' }); const items = await table.getItems(); // Array of proxied controls const firstItem = items[0]; const cells = await firstItem.getCells(); // Array of proxied controls const text = await cells[2].getText(); // 'Active' ``` ## Built-In Safe Methods These methods have dedicated implementations with proper event firing and fallback logic: | Method | Description | | ---------------------- | ----------------------------------------------------------------- | | `press()` | Fires press/tap event via interaction strategy | | `enterText(value)` | Sets value + fires liveChange + change events | | `select(key)` | Selects item by key or text | | `getAggregation(name)` | Returns aggregation as proxied control array | | `exec(fn, ...args)` | Executes an arbitrary function on the browser-side control | | `getControlMetadata()` | Returns control metadata (type, properties, aggregations, events) | | `getControlInfoFull()` | Returns full control introspection data | | `retrieveMembers()` | Returns all public members of the control | ## The Method Blacklist Praman blocks 71 static methods and 2 dynamic rules to prevent dangerous operations: ```typescript // These throw ControlError with code 'ERR_CONTROL_METHOD': await button.destroy(); // Lifecycle method await button.rerender(); // Rendering internal await button.setParent(other); // Tree manipulation await button.placeAt('container'); // DOM manipulation ``` The blacklist includes: - **Lifecycle methods** — `destroy`, `init`, `exit`, `onBeforeRendering`, `onAfterRendering` - **Internal methods** — `_*` (any method starting with underscore) - **Tree manipulation** — `setParent`, `insertAggregation`, `removeAllAggregation` - **Rendering** — `rerender`, `invalidate`, `placeAt` - **DOM access** — `getDomRef`, `$` (returns non-serializable DOM nodes) When a blacklisted method is called, the error includes suggestions for safe alternatives. ## Custom Function Execution For edge cases where you need browser-side logic that no proxy method covers, use `exec()`: ```typescript const control = await ui5.control({ id: 'mySmartField' }); // Execute arbitrary function on the browser-side control const customData = await control.exec((ctrl) => { const innerControl = ctrl.getInnerControls()[0]; return { type: innerControl.getMetadata().getName(), value: innerControl.getValue(), }; }); ``` The function passed to `exec()` runs inside `page.evaluate()` — it has access to the browser environment and the UI5 control as its first argument. Additional arguments are serialized and passed through. ## Anti-Thenable Guard JavaScript's `await` operator checks for a `then` property on any object. If a UI5 control happens to have a `then` method or property, `await proxy` would accidentally consume it. Praman's proxy includes an anti-thenable guard: ```typescript // The proxy intercepts 'then' access and returns undefined // This prevents accidental Promise consumption: const control = await ui5.control({ id: 'myControl' }); // Without the guard, this second await would break: const text = await control.getText(); ``` ## Control Introspection Use introspection methods to understand a control's capabilities at runtime: ```typescript const control = await ui5.control({ id: 'unknownControl' }); // Full metadata const meta = await control.getControlMetadata(); console.log(meta.controlType); // 'sap.m.Input' console.log(meta.properties); // ['value', 'placeholder', 'enabled', ...] console.log(meta.aggregations); // ['tooltip', 'customData', ...] console.log(meta.events); // ['change', 'liveChange', ...] // All public members const members = await control.retrieveMembers(); console.log(members.methods); // ['getValue', 'setValue', 'getEnabled', ...] ``` ## Comparison: wdi5 WDI5Control vs Praman UI5ControlBase | Aspect | wdi5 WDI5Control | Praman UI5ControlBase | | -------------------- | ------------------------------ | -------------------------------------------------------------------------------- | | **Proxy mechanism** | Class with `getProperty(name)` | ES Proxy with `get` trap | | **Method calls** | `control.getProperty('value')` | `control.getValue()` (direct) | | **Return handling** | Manual unwrapping | Auto-detect 7 return types | | **Blacklist** | Partial (small list) | 71 static + 2 dynamic rules | | **Chaining** | Limited, must re-wrap | Automatic proxy wrapping | | **Type safety** | Partial TypeScript | Full TypeScript with [199 typed control interfaces](/docs/guides/typed-controls) | | **Introspection** | Basic | `getControlMetadata()`, `retrieveMembers()`, `getControlInfoFull()` | | **Custom execution** | `executeMethod()` | `exec(fn, ...args)` | | **Step decoration** | None | Every call wrapped in `test.step()` | | **Caching** | None | LRU cache (200 entries, 5s TTL) | The key difference: wdi5 uses a class-based approach where you call generic methods like `getProperty('value')`. Praman uses an ES Proxy where you call `getValue()` directly — matching the UI5 API surface exactly. ## Performance Considerations - **Proxy creation is cheap** — the proxy is a thin wrapper around a control ID string - **Method calls cross the process boundary** — each call to the proxy invokes `page.evaluate()`, which is a round-trip to the browser. Batch operations when possible. - **Caching** — repeated `ui5.control()` calls with the same selector hit the LRU cache (200 entries, 5s TTL), returning the existing proxy without re-discovery - **Aggregation results** — `getItems()` on a table with 1000 rows returns 1000 proxies. Use `ui5.table.getRows()` for bulk data access instead. --- ## Lifecycle Hook Extensibility ## Overview Praman leverages Playwright's built-in extensibility mechanisms rather than a custom event emitter or plugin system. This guide covers how to extend praman's test lifecycle using Playwright-native patterns. ## 1. Request Interception with `page.route()` Intercept, modify, or mock HTTP requests at the network level. ```typescript test('mock OData response', async ({ page }) => { // Intercept OData entity set requests await page.route('**/sap/opu/odata/sap/API_BUSINESS_PARTNER/**', async (route) => { await route.fulfill({ status: 200, contentType: 'application/json', body: JSON.stringify({ d: { results: [{ BusinessPartner: 'BP001', BusinessPartnerName: 'Test Inc.' }] }, }), }); }); // Test runs with mocked data await page.goto('/app#BusinessPartner-manage'); }); ``` Praman's built-in `requestInterceptor` fixture uses this pattern to block WalkMe and analytics requests automatically. ## 2. Fixture Setup/Teardown with `use()` Playwright fixtures provide deterministic setup and teardown via the `use()` callback. Code before `use()` is setup; code after is teardown (guaranteed to run, even on failure). ```typescript const test = base.extend({ // Custom fixture with setup and teardown testOrder: async ({ page, ui5 }, use) => { // SETUP: create test data const orderId = await createTestOrder(page); // Pass to test await use(orderId); // TEARDOWN: always runs, even if test fails await deleteTestOrder(page, orderId); }, }); test('verify order', async ({ testOrder }) => { // testOrder contains the created order ID }); ``` ## 3. Browser Event Listeners with `page.on()` React to browser events like navigation, console messages, or dialog prompts. ```typescript test('track navigation events', async ({ page }) => { const navigations: string[] = []; // Listen for main frame navigations page.on('framenavigated', (frame) => { if (frame === page.mainFrame()) { navigations.push(frame.url()); } }); // Listen for console errors from UI5 page.on('console', (msg) => { if (msg.type() === 'error') { console.warn(`UI5 console error: ${msg.text()}`); } }); await page.goto('/app#PurchaseOrder-manage'); }); ``` Praman's `ui5Stability` fixture uses this pattern to call `waitForUI5Stable()` on main frame navigations. ## 4. Test Hooks with `beforeEach` / `afterEach` Standard Playwright hooks for cross-cutting concerns. ```typescript test.beforeEach(async ({ page }) => { // Runs before every test — e.g., ensure clean state await page.evaluate(() => { sessionStorage.clear(); }); }); test.afterEach(async ({ page }, testInfo) => { // Runs after every test — e.g., capture screenshot on failure if (testInfo.status !== testInfo.expectedStatus) { await page.screenshot({ path: `screenshots/${testInfo.title}.png` }); } }); ``` ## 5. Step Instrumentation with `test.step()` Group operations into named steps for Playwright trace viewer and HTML reporter. ```typescript test('create purchase order', async ({ page, ui5 }) => { await test.step('Navigate to PO creation', async () => { await page.goto('/app#PurchaseOrder-manage'); }); await test.step('Fill header data', async () => { await ui5.fill({ id: 'supplierInput' }, 'Supplier A'); await ui5.fill({ id: 'companyCodeInput' }, '1000'); }); await test.step('Verify creation', async () => { await expect(page.locator('ui5=sap.m.MessageToast')).toBeVisible(); }); }); ``` Praman's `@ui5Step` decorator uses `test.step({ box: true })` to automatically wrap handler methods in trace-visible steps with internal stack frames filtered. ## Combining Patterns These patterns compose naturally. A typical SAP test fixture combines all four: ```typescript const test = base.extend({ sapApp: async ({ page, ui5 }, use) => { // SETUP: Mock slow service, navigate, wait await page.route('**/slow-analytics/**', (route) => route.abort()); page.on('framenavigated', (frame) => { if (frame === page.mainFrame()) { // Log navigation for debugging } }); await page.goto('/app#MyApp-display'); await use({ page, ui5 }); // TEARDOWN: Clean up test data }, }); ``` ## Why No Custom Event System? Playwright's fixture model, `page.route()`, `page.on()`, and `test.step()` together cover every lifecycle hook scenario. A custom event emitter would: - Duplicate Playwright's built-in capabilities - Add maintenance burden and API surface - Require documentation for a non-standard pattern - Confuse users familiar with Playwright's patterns Praman follows Playwright's philosophy: use the platform's native extensibility rather than building framework-specific abstractions. --- ## Capability Registry Machine-readable registry of every Praman capability. Each entry documents **what** the capability does, **what it guarantees**, which **APIs** it exposes, how it can **fail**, and an **agent hint** for AI-driven test generation. :::info Audiences This registry serves three audiences: - **SAP Test Engineers** — find the right API for a task, understand guarantees and failure modes - **QA Leads & Evaluators** — audit coverage, map capabilities to business processes - **AI Agents** — use `agent_hint` fields to select capabilities and avoid anti-patterns ::: ## Overview | Metric | Count | | ---------------------- | ---------------------------------------------- | | **Total Capabilities** | 36 | | **Total Public APIs** | 231 | | **Category Codes** | 18 | | **Source of Truth** | `playwright-praman/src/**/*.ts` (exports only) | | **Version** | 1.0.1 | ### Category Codes | Code | Domain | | ----- | ------------------------------ | | LOC | Control Discovery & Locators | | ACT | Interactions & Actions | | WAIT | Waiting & Synchronization | | ASRT | Assertions & Matchers | | NAV | Navigation & Routing | | AUTH | Authentication & Session Setup | | DATA | Data & Model Inspection | | TBL | Tables & Lists | | DLG | Dialogs, Popovers & Overlays | | DTP | Date & Time Controls | | FE | Fiori Elements Helpers | | FLP | FLP Platform Services | | VOCAB | Business Vocabulary | | INT | SAP Business Intents | | FIX | Fixtures & Test Configuration | | AI | AI Agent Operability | | DX | Developer Experience & Tooling | | MIG | Migration & Compatibility | --- ## LOC: Control Discovery & Locators ### UI5-LOC-001 — Locate controls by metadata Find UI5 controls using type, properties, binding path, or ID without CSS selectors. **Guarantees:** - Stable across UI rerenders, theme changes, and view recreations - No dependency on generated DOM IDs or CSS classes - Falls back to Playwright locator for non-UI5 DOM elements automatically - Cached per-selector within a test to avoid redundant browser roundtrips **APIs:** | API | Description | | ----------------------------- | --------------------------------------------------------------- | | `ui5.control(selector)` | Find single control by UI5Selector — `sap.ui.test.RecordReplay` | | `ui5.controls(selector)` | Find all matching controls — `sap.ui.core.Element.registry` | | `ui5.waitFor(selector, opts)` | Poll until control appears — custom polling loop | | `ui5.inspect(selector)` | Return full metadata without proxy — `sap.ui.core.Element` | **Failures:** - Control not rendered — throws `ControlError` if called before UI5 bootstraps - Ambiguous match — returns first match; use `controlType` + `properties` to narrow - Stale ID after navigation — cache is invalidated on frame navigation events - Timeout waiting for control — throws `TimeoutError` after configurable deadline :::tip Agent Hint Use when user needs to find, locate, or select a UI5 control on screen. Do not use for raw DOM queries, non-UI5 web elements, or CSS/XPath selectors. ::: ### UI5-LOC-002 — Custom selector engine (ui5=) Use Playwright's native selector syntax with `ui5=` prefix for control queries. **Guarantees:** - Registered once per worker via Playwright `selectors.register()` - Supports `controlType` and `ID` via `data-sap-ui` attributes - Compatible with all Playwright locator methods (`click`, `fill`, `waitFor`) **APIs:** | API | Description | | ------------------------------------- | ----------------------------------- | | `page.locator('ui5=sap.m.Button#id')` | Playwright selector with ui5 engine | | `createUI5SelectorEngineScript()` | Factory for engine registration | **Failures:** - Engine not registered — if worker fixture not loaded, `ui5=` prefix throws - Attribute missing — pre-bootstrap elements lack `data-sap-ui` attributes :::tip Agent Hint Use when integrating UI5 control selection with Playwright's native locator API. Do not use when full UI5Selector object with properties/bindings is needed. ::: ### UI5-LOC-003 — Inspect control metadata Read full control properties, bindings, aggregations, and type without interaction. **Guarantees:** - Returns `controlType`, all properties with current values, aggregation names - Includes binding paths for data-bound properties - Does not create interactive proxy — read-only introspection **APIs:** | API | Description | | ----------------------- | ---------------------------------------------------------- | | `ui5.inspect(selector)` | Full metadata including properties, bindings, aggregations | **Failures:** - Control destroyed between discovery and inspection — throws `ControlError` - Bridge not injected — auto-injects on first call :::tip Agent Hint Use when user needs to debug, assert metadata, or analyze a control's state. Do not use for clicking/filling — use `control()` for interactive proxies. ::: --- ## ACT: Interactions & Actions ### UI5-ACT-001 — Control interactions Click, fill, select, check, and clear UI5 controls via pluggable interaction strategies. **Guarantees:** - Waits for UI5 stability before every action - Executes via configurable strategy (DOM-first, OPA5, or UI5-native) - Each action appears as a named step in Playwright trace viewer - Method blacklist prevents calling destructive SAP API methods **APIs:** | API | Description | | --------------------------- | --------------------------------------------------------------- | | `ui5.click(selector)` | Click a button, link, or control — `InteractionStrategy.press` | | `ui5.fill(selector, value)` | Type text into input/textarea — `InteractionStrategy.enterText` | | `ui5.press(selector)` | Alias for click | | `ui5.select(selector, key)` | Pick option in dropdown/combo — `InteractionStrategy.select` | | `ui5.check(selector)` | Enable a checkbox — `setSelected(true)` | | `ui5.uncheck(selector)` | Disable a checkbox — `setSelected(false)` | | `ui5.clear(selector)` | Clear text from input — `enterText('')` | **Failures:** - Control not found — throws `ControlError` with selector details - Control not interactable — strategy-level error if control is disabled/invisible - Input validation rejection — `fireChange` may trigger UI5 server-side validation :::tip Agent Hint Use when user needs to click, type, select, check, or clear a UI5 control. Do not use for reading values — use `getText`/`getValue` instead. ::: ### UI5-ACT-002 — Read control values Read displayed text or current value from UI5 controls. **Guarantees:** - Waits for UI5 stability before reading - Reads via `page.evaluate()` bridge to SAP API `getText()`/`getValue()` **APIs:** | API | Description | | ------------------------ | ------------------------------------------ | | `ui5.getText(selector)` | Read text property — `control.getText()` | | `ui5.getValue(selector)` | Read value property — `control.getValue()` | **Failures:** - Control has no text/value method — returns undefined or throws - Value is stale — read may return old value if called during async model update :::tip Agent Hint Use when user needs to read or assert the text/value displayed in a UI5 control. Do not use for metadata inspection — use `inspect()` instead. ::: ### UI5-ACT-003 — Footer bar actions Click standard Fiori footer bar buttons (Save, Edit, Cancel, Delete, Create, Apply). **Guarantees:** - Targets buttons by visible text in the `.sapMBarChild` footer area - Each click appears as a named step in Playwright trace viewer **APIs:** | API | Description | | ------------------------- | ------------------- | | `ui5Footer.clickSave()` | Click Save button | | `ui5Footer.clickEdit()` | Click Edit button | | `ui5Footer.clickCancel()` | Click Cancel button | | `ui5Footer.clickDelete()` | Click Delete button | | `ui5Footer.clickCreate()` | Click Create button | | `ui5Footer.clickApply()` | Click Apply button | **Failures:** - Button not in footer — throws `ControlError` if button text not found - Wrong view mode — Edit button only appears in display mode, Save in edit mode :::tip Agent Hint Use when user needs to click Save, Edit, Cancel, or other standard footer buttons. Do not use for custom buttons — use `ui5.click()` with a selector instead. ::: --- ## WAIT: Waiting & Synchronization ### UI5-WAIT-001 — UI5 framework synchronization Wait for the UI5 rendering pipeline to complete before acting on controls. **Guarantees:** - Checks `sap.ui.getCore().getUIDirty()` for pending renders - Auto-wait runs before every module method call (table, dialog, date, odata) - Configurable timeout with per-operation override - Auto-stability fixture waits after every main frame navigation **APIs:** | API | Description | | --------------------------------- | ------------------------------------ | | `ui5.waitForUI5(timeout)` | Explicit wait for UI5 stability | | `waitForUI5Stable(page, opts)` | Standalone utility function | | `waitForUI5Bootstrap(page, opts)` | Wait for `sap.ui.getCore()` to exist | **Failures:** - UI5 not loaded — returns immediately if `sap.ui` is undefined (non-UI5 page) - Infinite pending — times out if UI5 keeps scheduling micro-tasks :::tip Agent Hint Use when user needs to wait for UI5 to finish rendering after navigation or data load. Do not use for waiting for a specific control — use `ui5.waitFor(selector)` instead. ::: ### UI5-WAIT-002 — Request interception Auto-block WalkMe, analytics, and tracking scripts during test execution. **Guarantees:** - Auto-enabled via stability fixture — no manual activation needed - Blocks patterns from `DEFAULT_IGNORE_PATTERNS` plus custom `ignoreAutoWaitUrls` - Routes are cleaned up in teardown to avoid handler leaks **APIs:** | API | Description | | ----------------------------------- | ------------------------------------------------- | | `requestInterceptor` (auto fixture) | Blocks configured URL patterns via `page.route()` | **Failures:** - Pattern too broad — may accidentally block legitimate app requests - WalkMe loaded before intercept — first-load scripts may already execute :::tip Agent Hint Use when user reports WalkMe/analytics interference during test execution. Do not use for custom request mocking — use Playwright's `page.route()` directly. ::: --- ## ASRT: Assertions & Matchers ### UI5-ASRT-001 — UI5 control matchers Assert UI5 control properties, visibility, enabled state, and value state with auto-retry. **Guarantees:** - All matchers auto-retry via polling until timeout (web-first assertion pattern) - Registered automatically via worker fixture — no manual setup - Descriptive failure messages include control ID, expected, and actual values - Support both exact match and RegExp patterns for text assertions **APIs:** | API | Description | | ---------------------------------------------- | ---------------------------------------------- | | `expect(proxy).toHaveUI5Text(expected)` | Assert text property | | `expect(proxy).toBeUI5Visible()` | Assert `visible === true` | | `expect(proxy).toBeUI5Enabled()` | Assert `enabled === true` | | `expect(proxy).toHaveUI5Property(name, value)` | Assert any property | | `expect(proxy).toHaveUI5ValueState(state)` | Assert valueState (None/Error/Warning/Success) | | `expect(proxy).toHaveUI5Binding(prop, path?)` | Assert data binding exists | | `expect(proxy).toBeUI5ControlType(type)` | Assert fully qualified control type | **Failures:** - Control destroyed mid-assertion — polling stops, last error surfaces - Property does not exist — returns undefined, assertion fails with clear message :::tip Agent Hint Use when user needs to assert properties, visibility, or state of a UI5 control. Do not use for non-UI5 DOM elements — use Playwright's built-in `expect` instead. ::: ### UI5-ASRT-002 — UI5 table matchers Assert table row count, cell text, and selected row count with auto-retry. **Guarantees:** - Navigates table structure via items/cells aggregations - Supports string and RegExp matching for cell text - Auto-retry via polling until timeout **APIs:** | API | Description | | ------------------------------------------------- | -------------------------------------- | | `expect(proxy).toHaveUI5RowCount(n)` | Assert row count via items aggregation | | `expect(proxy).toHaveUI5CellText(row, col, text)` | Assert cell text at position | | `expect(proxy).toHaveUI5SelectedRows(n)` | Assert selected row count | **Failures:** - Row/column out of bounds — returns descriptive error with actual dimensions - Table not loaded — empty items aggregation until OData load completes :::tip Agent Hint Use when user needs to verify table row count, cell content, or selection state. Do not use for table interactions (click row, sort) — use `ui5.table` methods instead. ::: --- ## NAV: Navigation & Routing ### UI5-NAV-001 — FLP navigation Navigate between SAP Fiori Launchpad apps by hash, tile, intent, or search. **Guarantees:** - Each navigation appears as a named step in Playwright trace viewer - Bridge injection state auto-resets on frame navigation - Supports semantic object intents with query parameters - Configurable base URL injection from auth config **APIs:** | API | Description | | ------------------------------------------------- | --------------------------------------------------------------- | | `ui5Navigation.navigateToApp(appId)` | Navigate by semantic hash | | `ui5Navigation.navigateToTile(title)` | Click FLP tile by title | | `ui5Navigation.navigateToIntent(intent, params?)` | Semantic object + action (`intent: { semanticObject, action }`) | | `ui5Navigation.navigateToHash(hash)` | Direct hash navigation | | `ui5Navigation.navigateToHome()` | Return to FLP home | | `ui5Navigation.navigateBack()` | Browser history back | | `ui5Navigation.navigateForward()` | Browser history forward | | `ui5Navigation.searchAndOpenApp(title)` | Shell search bar + open | | `ui5Navigation.getCurrentHash()` | Read current URL hash | **Failures:** - App not found — tile or search may not find the target app - Shell not loaded — navigation fails before FLP bootstrap completes - Hash collision — multiple apps may respond to the same semantic object :::tip Agent Hint Use when user needs to navigate to a Fiori app, tile, or FLP home page. Do not use for in-app routing (sections, tabs) — use objectPage helpers instead. ::: ### UI5-NAV-002 — Shell header operations Interact with the Fiori Launchpad shell header (home, user menu, notifications). **Guarantees:** - Targets well-known FLP shell DOM IDs (`#shell-header`, `#meAreaHeaderButton`) - Works across Fiori 2.0 and 3.0 shell layouts **APIs:** | API | Description | | ------------------------------ | ------------------------------ | | `ui5Shell.expectShellHeader()` | Assert shell header is visible | | `ui5Shell.clickHome()` | Navigate to FLP home via logo | | `ui5Shell.openUserMenu()` | Open user avatar menu | | `ui5Shell.openNotifications()` | Open notifications panel | **Failures:** - Shell not rendered — throws `NavigationError` if `#shell-header` is missing - Custom shell — non-standard FLP shells may use different selectors :::tip Agent Hint Use when user needs to interact with FLP shell header (home, user menu, notifications). Do not use for in-app navigation — use `ui5Navigation` instead. ::: --- ## AUTH: Authentication & Session Setup ### UI5-AUTH-001 — SAP authentication strategies Authenticate to SAP systems using pluggable strategies (on-prem, Cloud SAML, O365, API, cert). **Guarantees:** - Strategy auto-detected from URL pattern or explicit config - Custom strategies can be registered via `registerAuthStrategy()` - Session tracked with `authenticatedAt` timestamp - Auto-logout during fixture teardown if session is active **APIs:** | API | Description | | ------------------------------------- | ------------------------------- | | `sapAuth.login(page, config)` | Execute auth flow via strategy | | `sapAuth.logout(page)` | Server-side logout | | `sapAuth.getSessionInfo()` | Read session metadata | | `createAuthStrategy(config)` | Factory for built-in strategies | | `registerAuthStrategy(name, factory)` | Register custom strategy | | `detectSystemType(config)` | Auto-detect on-prem vs cloud | **Failures:** - Invalid credentials — strategy-specific error after login page interaction - SAML redirect timeout — Cloud SAML IdP may be slow to respond - Session expired — re-authentication needed mid-test - Multi-tenant URL construction — incorrect subdomain causes 404 :::tip Agent Hint Use when user needs to log in/out of an SAP system or configure auth strategies. Do not use for non-SAP authentication — use Playwright's built-in auth instead. ::: ### UI5-AUTH-002 — Authentication state checks Verify authentication state without performing login/logout. **Guarantees:** - Page-level checks via `page.evaluate()` — no server roundtrip - Checks multiple indicators (UI5 loaded, shell visible, login page, user menu) **APIs:** | API | Description | | -------------------------- | ----------------------------------- | | `isAuthenticated(page)` | Composite check for logged-in state | | `isLoginPageVisible(page)` | Detect login form on screen | | `isShellVisible(page)` | Detect FLP shell header | | `isUI5Loaded(page)` | Detect `sap.ui.getCore()` | | `isUserMenuVisible(page)` | Detect user avatar button | **Failures:** - Partial load — shell visible but UI5 not fully bootstrapped - Custom login page — non-standard login forms may not be detected :::tip Agent Hint Use when user needs to check if the current page is authenticated. Do not use to perform login — use `sapAuth.login()` instead. ::: --- ## DATA: Data & Model Inspection ### UI5-DATA-001 — OData model queries Read OData model data, properties, and pending changes from the UI5 model layer. **Guarantees:** - Reads via `page.evaluate()` from the UI5 OData model (not HTTP) - Supports custom model names for multi-model apps - CSRF token fetched via HEAD request for write operations **APIs:** | API | Description | | ---------------------------------- | ------------------------------------------------------------ | | `ui5.odata.getModelData(path)` | Read entity or collection — `ODataModel.getData()` | | `ui5.odata.getModelProperty(path)` | Read single property — `ODataModel.getProperty()` | | `ui5.odata.waitForLoad(opts)` | Wait for OData requests to complete | | `ui5.odata.fetchCSRFToken(url)` | Fetch token for write operations | | `ui5.odata.getEntityCount(path)` | Count entities at model path | | `ui5.odata.hasPendingChanges()` | Check for unsaved changes — `ODataModel.hasPendingChanges()` | **Failures:** - Model not found — default model may not be OData type - Path not bound — model path has no data if view hasn't loaded it - CSRF token expired — token has limited lifetime :::tip Agent Hint Use when user needs to read OData model data or check for pending changes. Do not use for HTTP CRUD — use OData HTTP methods for direct service calls. ::: ### UI5-DATA-002 — OData HTTP operations Execute CRUD operations and function imports against OData services via HTTP. **Guarantees:** - Uses Playwright's `page.request` API (not the UI5 model layer) - Supports OData V2 and V4 URL conventions - CSRF token handled automatically for mutating operations **APIs:** | API | Description | | ----------------------------------------------- | -------------------------------------- | | `ui5.odata.createEntity(url, set, data)` | POST new entity | | `ui5.odata.updateEntity(url, set, key, data)` | PUT/PATCH entity | | `ui5.odata.deleteEntity(url, set, key)` | DELETE entity | | `ui5.odata.queryEntities(url, set, opts)` | GET with `$filter`/`$select`/`$expand` | | `ui5.odata.callFunctionImport(url, fn, params)` | Invoke function import | **Failures:** - Service unavailable — 404 if OData service not activated in SAP - Authorization error — 403 for missing SAP roles - Entity not found — 404 on update/delete of non-existent key :::tip Agent Hint Use when user needs to create, update, delete, or query OData entities via HTTP. Do not use for reading model-layer data — use `getModelData()` instead. ::: --- ## TBL: Tables & Lists ### UI5-TBL-001 — Table operations Read, select, filter, sort, and export rows in `sap.m.Table` and `sap.ui.table.Table`. **Guarantees:** - Auto-detects table type (responsive vs grid vs analytical) via `detectTableType()` - Pre-action UI5 stability wait on every method call - Each operation wrapped in `test.step()` for trace viewer - Supports Smart Table variants **APIs:** | API | Description | | -------------------------------------------------- | ---------------------------- | | `ui5.table.getRows(id)` | Get all row data | | `ui5.table.getRowCount(id)` | Count visible rows | | `ui5.table.getCellValue(id, row, col)` | Read cell text | | `ui5.table.getData(id)` | Get full table data as array | | `ui5.table.selectRow(id, row)` | Select a row by index | | `ui5.table.selectAll(id)` | Select all rows | | `ui5.table.deselectAll(id)` | Deselect all rows | | `ui5.table.getSelectedRows(id)` | Get selected row indices | | `ui5.table.waitForData(id)` | Wait for rows to load | | `ui5.table.findRowByValues(id, criteria)` | Find row by column values | | `ui5.table.clickRow(id, row)` | Click a row | | `ui5.table.selectRowByValues(id, values)` | Select by column match | | `ui5.table.ensureRowVisible(id, row)` | Scroll to row | | `ui5.table.setTableCellValue(id, row, col, value)` | Write cell value | | `ui5.table.getColumnNames(id)` | List column headers | | `ui5.table.getCellByColumnName(id, row, col)` | Read by column name | | `ui5.table.filterByColumn(id, col, value)` | Apply column filter | | `ui5.table.sortByColumn(id, col)` | Toggle column sort | | `ui5.table.getSortOrder(id, col)` | Read sort direction | | `ui5.table.getFilterValue(id, col)` | Read active filter value | | `ui5.table.exportData(id)` | Trigger table export | | `ui5.table.clickSettings(id)` | Open table settings dialog | **Failures:** - Table not found — throws if control ID doesn't match a table - Row index out of bounds — error if `row >= table length` - Data not loaded — empty rows before OData response arrives :::tip Agent Hint Use when user needs to interact with a UI5 table (read, select, filter, sort rows). Do not use for Fiori Elements table helpers — use `fe.table` instead. ::: --- ## DLG: Dialogs, Popovers & Overlays ### UI5-DLG-001 — Dialog management Wait for, confirm, dismiss, and inspect open UI5 dialogs and popovers. **Guarantees:** - `searchOpenDialogs: true` selector flag searches the UI5 static area - Pre-action UI5 stability wait on every method call - Each operation wrapped in `test.step()` for trace viewer **APIs:** | API | Description | | ------------------------------------ | ------------------------------------ | | `ui5.dialog.waitFor(opts)` | Wait for any dialog to appear | | `ui5.dialog.confirm(opts)` | Click positive (OK/Confirm) button | | `ui5.dialog.dismiss(opts)` | Click negative (Cancel/Close) button | | `ui5.dialog.isOpen(dialogId)` | Check if specific dialog is open | | `ui5.dialog.getOpen()` | List all currently open dialogs | | `ui5.dialog.getButtons(dialogId)` | List available dialog buttons | | `ui5.dialog.waitForClosed(dialogId)` | Wait for dialog to close | **Failures:** - No dialog open — `waitFor` times out if no dialog appears - Button text mismatch — `confirm`/`dismiss` assumes standard button texts - Popover vs Dialog — some overlays use `Popover`, not `Dialog` :::tip Agent Hint Use when user needs to interact with UI5 dialogs, message boxes, or popovers. Do not use for in-page sections or panels — use objectPage helpers. ::: --- ## DTP: Date & Time Controls ### UI5-DTP-001 — Date and time picker controls Set and read DatePicker, DateRangeSelection, and TimePicker control values. **Guarantees:** - Handles SAP date format conversion via `formatDateForUI5()` - Supports `Date` objects, ISO strings, and epoch numbers as input - `setAndValidate` combines set + read-back assertion **APIs:** | API | Description | | ---------------------------------------- | ------------------------ | | `ui5.date.setDatePicker(id, date, opts)` | Set date value | | `ui5.date.getDatePicker(id)` | Read current date value | | `ui5.date.setDateRange(id, start, end)` | Set date range selection | | `ui5.date.getDateRange(id)` | Read date range values | | `ui5.date.setTimePicker(id, time)` | Set time value | | `ui5.date.getTimePicker(id)` | Read current time value | | `ui5.date.setAndValidate(id, date)` | Set and verify roundtrip | **Failures:** - Invalid date format — format mismatch between input and UI5 locale - Date picker closed — value may not persist if picker not properly focused :::tip Agent Hint Use when user needs to set or read date/time picker controls in SAP UI5 forms. Do not use for non-date inputs — use `ui5.fill()` for regular text fields. ::: --- ## FE: Fiori Elements Helpers ### UI5-FE-001 — List Report operations Filter, search, navigate, and manage variants in Fiori Elements List Report pages. **Guarantees:** - Works with standard FE-generated List Report templates - Targets FE-specific DOM structure (FilterBar, SmartTable) **APIs:** | API | Description | | ---------------------------------------- | ----------------------------------- | | `fe.listReport.getTable()` | Get the main List Report table info | | `fe.listReport.getFilterBar()` | Get the FilterBar control info | | `fe.listReport.setFilter(field, value)` | Set a filter bar field value | | `fe.listReport.search()` | Execute the Go/Search button | | `fe.listReport.clearFilters()` | Reset all filter bar fields | | `fe.listReport.navigateToItem(rowIndex)` | Navigate to detail page | | `fe.listReport.getVariants()` | List available view variants | | `fe.listReport.selectVariant(name)` | Activate a variant | | `fe.listReport.getFilterValue(field)` | Read a filter field's value | **Failures:** - No FilterBar — page may not use standard FE FilterBar component - Variant not found — variant name doesn't match available options :::tip Agent Hint Use when user needs to operate on a Fiori Elements List Report page. Do not use for custom-built list pages — use `ui5.table` and `ui5.control` instead. ::: ### UI5-FE-002 — Object Page operations Edit, save, navigate sections, and inspect headers in Fiori Elements Object Pages. **Guarantees:** - Works with standard FE-generated Object Page templates - Supports section navigation by title or ID **APIs:** | API | Description | | ---------------------------------------- | ---------------------------- | | `fe.objectPage.clickEdit()` | Enter edit mode | | `fe.objectPage.clickSave()` | Save changes | | `fe.objectPage.clickButton(name)` | Click any Object Page button | | `fe.objectPage.navigateToSection(title)` | Scroll to section | | `fe.objectPage.getSections()` | List all sections | | `fe.objectPage.getSectionData(title)` | Read section content | | `fe.objectPage.getHeaderTitle()` | Read header title | | `fe.objectPage.isInEditMode()` | Check edit/display mode | **Failures:** - Section not found — title doesn't match any rendered section - Not in edit mode — save fails if not switched to edit first :::tip Agent Hint Use when user needs to operate on a Fiori Elements Object Page. Do not use for custom detail pages — use `ui5.control` and `ui5.click` instead. ::: ### UI5-FE-003 — FE table and list helpers Read rows, cells, and interact with Fiori Elements tables and lists. **Guarantees:** - FE-specific helpers that understand FE table/list DOM structure - Separate from core table module — optimized for FE templates **APIs:** | API | Description | | ----------------------------------------- | --------------------------------- | | `fe.table.getRowCount(id)` | Count FE table rows | | `fe.table.getCellValue(id, row, col)` | Read FE cell value by column name | | `fe.table.findRow(id, values)` | Find row matching column values | | `fe.table.clickRow(id, row)` | Click an FE table row | | `fe.table.getColumnNames(id)` | List FE table column names | | `fe.list.getItemCount(id)` | Count list items | | `fe.list.getItemTitle(id, index)` | Read list item title | | `fe.list.findItemByTitle(id, title)` | Find item by title text | | `fe.list.clickItem(id, index)` | Click a list item | | `fe.list.selectItem(id, index, selected)` | Toggle list item selection | **Failures:** - Not a FE table — helpers assume FE-generated table structure - Column name mismatch — column names are case-sensitive :::tip Agent Hint Use when user needs to read or interact with Fiori Elements tables or lists. Do not use for core `sap.m.Table` — use `ui5.table` methods instead. ::: --- ## FIX: Fixtures & Test Configuration ### UI5-FIX-001 — Merged test fixtures Single import provides all Praman fixtures (ui5, navigation, auth, AI, FE, etc.). **Guarantees:** - `import { test, expect } from 'playwright-praman'` gives everything - Fixtures composed via Playwright's `mergeTests()` — no import conflicts - Worker fixtures (config, logger, tracer) created once per worker - Test fixtures (ui5, sapAuth) created per test with proper teardown **APIs:** | API | Description | | ----------------- | ----------------------------------------------------------- | | `test` | Merged Playwright test with all fixtures | | `expect` | Playwright expect with UI5 matchers registered | | `coreTest` | Standalone core fixtures (config, logger, ui5) | | `authTest` | Standalone auth fixtures (sapAuth) | | `navTest` | Standalone navigation fixtures (ui5Navigation, btpWorkZone) | | `stabilityTest` | Standalone stability fixtures (auto-wait, interception) | | `feTest` | Standalone Fiori Elements fixtures (fe) | | `aiTest` | Standalone AI fixtures (pramanAI) | | `intentTest` | Standalone intent fixtures (intent) | | `moduleTest` | Core + table/dialog/date/odata sub-namespaces | | `shellFooterTest` | Shell + footer fixtures (ui5Shell, ui5Footer) | | `flpLocksTest` | FLP lock management fixtures (flpLocks) | | `flpSettingsTest` | FLP user settings fixtures (flpSettings) | | `testDataTest` | Test data generation fixtures (testData) | **Failures:** - Missing Playwright version — requires `>=1.57.0`, asserted at worker init - Config file not found — `loadConfig()` uses defaults with warning :::tip Agent Hint Use when user needs to understand which fixtures are available or how to import. Do not use for specific fixture usage — refer to the relevant capability. ::: ### UI5-FIX-002 — Configuration management Define and load validated, frozen Praman configuration with Zod schema. **Guarantees:** - Zod-validated with detailed error messages on invalid config - Configuration is `Readonly` — never mutated at runtime - Loaded once per worker via `loadConfig()` - Supports `praman.config.ts` file discovery **APIs:** | API | Description | | --------------------- | ----------------------------------------- | | `defineConfig(input)` | Create typed config with defaults applied | | `loadConfig(opts?)` | Load and validate config from file or env | **Failures:** - Invalid config — throws `ConfigError` with field-level validation details - File not found — falls back to defaults with structured warning :::tip Agent Hint Use when user needs to configure Praman timeouts, strategies, or auth settings. Do not use for Playwright config — this is Praman-specific configuration. ::: --- ## AI: AI Agent Operability ### UI5-AI-001 — Page discovery for AI agents Discover all UI5 controls on a page and build structured context for LLM prompts. **Guarantees:** - Returns `AiResponse` envelope with status, data, and metadata - Discovers control types, IDs, properties, and aggregations - Context includes UI5 version detection **APIs:** | API | Description | | -------------------------------- | -------------------------------------- | | `pramanAI.discoverPage(opts?)` | Find all controls on current page | | `pramanAI.buildContext()` | Enriched page context with UI5 version | | `discoverPage(page, opts)` | Standalone discovery function | | `buildPageContext(page, config)` | Standalone context builder | **Failures:** - Non-UI5 page — returns empty context with status indicating no UI5 - Too many controls — large pages may hit `evaluate()` payload limits :::tip Agent Hint Use when an AI agent needs to understand the current page structure. Do not use for manual test authoring — use `ui5.control`/`inspect` instead. ::: ### UI5-AI-002 — Capability and recipe registries Machine-readable registries mapping Praman capabilities and test recipes for AI agents. **Guarantees:** - Capabilities indexed by category code for agent tool selection - Recipes provide step-by-step patterns for common SAP test scenarios - Both registries loadable from generated YAML or programmatic API **APIs:** | API | Description | | -------------------- | ----------------------------------------- | | `CapabilityRegistry` | Query capabilities by ID or category | | `RecipeRegistry` | Query recipes by name, domain, or pattern | | `capabilities` | Pre-loaded capability data | | `recipes` | Pre-loaded recipe data | **Failures:** - Registry empty — capabilities/recipes not generated yet - Version mismatch — registry from different plugin version :::tip Agent Hint Use when an AI agent needs to select which Praman methods to call for a task. Do not use for human test authoring — read the documentation instead. ::: ### UI5-AI-003 — LLM service and agentic handler Connect to LLM providers and execute agentic test generation workflows. **Guarantees:** - Supports Anthropic and OpenAI via optional peer dependencies - Dynamic import — AI deps only loaded when `pramanAI` fixture is used - LLM connection cleaned up on fixture teardown **APIs:** | API | Description | | -------------------------- | ----------------------------------------------- | | `pramanAI.llm` | Raw LLM service for direct prompting | | `pramanAI.agentic` | AgenticHandler for autonomous test generation | | `createLlmService(config)` | Factory for LLM provider connection | | `AgenticHandler` | Orchestrates discovery + prompting + generation | **Failures:** - No API key — throws `AIError` if provider key not configured - Provider unreachable — network timeout on LLM call - Token limit — large page context may exceed provider token window :::tip Agent Hint Use when user needs AI-powered test generation or LLM integration. Do not use for manual test writing — use fixtures and intents instead. ::: --- ## VOCAB: Business Vocabulary ### UI5-VOCAB-001 — SAP business vocabulary resolution Resolve natural-language business terms to UI5 selectors across SAP domains. **Guarantees:** - Fuzzy matching with configurable threshold - Synonym resolution across 6 SAP domains (procurement, sales, finance, manufacturing, warehouse, quality) - Vocabulary loaded per-domain for token efficiency **APIs:** | API | Description | | ------------------------------- | ---------------------------------- | | `pramanAI.vocabulary` | VocabularyService instance | | `createVocabularyService()` | Factory for vocabulary service | | `vocabulary.loadDomain(domain)` | Load domain-specific terms | | `vocabulary.search(term)` | Fuzzy search across loaded domains | **Failures:** - Domain not loaded — search returns empty if domain not preloaded - Term ambiguous — multiple matches across domains - Vocabulary file missing — throws `VocabularyError` :::tip Agent Hint Use when user mentions SAP business terms (vendor, PO, material) and needs selector mapping. Do not use for technical UI5 control names — use `ui5.control` with `controlType`. ::: --- ## INT: SAP Business Intents ### UI5-INT-001 — SAP domain intent operations Execute high-level SAP business operations across 5 modules via typed intent functions. **Guarantees:** - Vocabulary domains auto-preloaded before `use()` — ready immediately - Typed input data for each operation (PO, SO, journal entry, etc.) - Returns `IntentResult` with status, message, and operation details - All intent functions use `test.step()` for Playwright trace integration **APIs:** | API | SAP Transaction | | ---------------------------------------------------- | --------------------------------- | | `intent.core.fillField(label, value)` | Fill form field by business label | | `intent.core.clickButton(text)` | Click button by label | | `intent.core.selectOption(label, option)` | Select from dropdown by label | | `intent.core.assertField(label, expected)` | Assert field value | | `intent.core.confirmAndWait()` | Click Confirm and wait for close | | `intent.core.waitForSave()` | Wait for save completion | | `intent.procurement.createPurchaseOrder(data)` | ME21N | | `intent.procurement.approvePurchaseOrder(data)` | PO approval | | `intent.procurement.searchPurchaseOrders(criteria)` | PO search | | `intent.procurement.createPurchaseRequisition(data)` | ME51N | | `intent.procurement.confirmGoodsReceipt(data)` | MIGO | | `intent.procurement.searchVendors()` | Vendor master search | | `intent.sales.createSalesOrder(data)` | VA01 | | `intent.sales.createQuotation(data)` | VA21 | | `intent.sales.approveQuotation(data)` | Quotation approval | | `intent.sales.searchSalesOrders(criteria)` | SO search | | `intent.sales.searchCustomers()` | Customer master search | | `intent.sales.checkDeliveryStatus(data)` | Delivery status check | | `intent.finance.createJournalEntry(data)` | FB50 | | `intent.finance.postVendorInvoice(data)` | FB60 | | `intent.finance.processPayment(data)` | F-53 | | `intent.manufacturing.createProductionOrder(data)` | CO01 | | `intent.manufacturing.confirmProductionOrder(data)` | CO11N | | `intent.masterData.createVendorMaster(data)` | XK01/BP | | `intent.masterData.createCustomerMaster(data)` | XD01/BP | | `intent.masterData.createMaterialMaster(data)` | MM01 | **Failures:** - App not found — intent navigates to wrong app if hash mapping fails - Field label not resolved — vocabulary miss for custom field labels - Mandatory field missing — SAP validation rejects incomplete data :::tip Agent Hint Use when user describes a SAP business operation (create PO, post invoice, etc.). Do not use for low-level control interactions — use `ui5.click`/`fill` instead. ::: --- ## FLP: FLP Platform Services ### UI5-FLP-001 — FLP lock management Query, count, and delete SAP SM12 lock entries via OData for test isolation. **Guarantees:** - Auto-cleanup of tracked locks during fixture teardown - CSRF token fetched automatically for delete operations - Non-fatal cleanup — failures logged as warnings, not thrown **APIs:** | API | Description | | -------------------------------------------- | ------------------------------------- | | `flpLocks.getLockEntries(username?)` | Query SM12 lock entries | | `flpLocks.getNumberOfLockEntries(username?)` | Count lock entries | | `flpLocks.deleteAllLockEntries(username?)` | Delete all matching locks | | `flpLocks.cleanup()` | Auto-release tracked locks (teardown) | **Failures:** - `SM12_SRV` not activated — throws `FLPError` with `ERR_FLP_API_UNAVAILABLE` - Permission denied — 403 if user lacks SM12 authorization - Lock already released — delete returns 404 (handled gracefully) :::tip Agent Hint Use when user needs to manage SAP lock entries during test setup/teardown. Do not use for application-level locking — this is system-level SM12 locks. ::: ### UI5-FLP-002 — FLP user settings Read SAP Fiori Launchpad user settings (language, date/time format, timezone). **Guarantees:** - Reads from `sap.ushell.Container.getUser()` — no HTTP roundtrip - `getAllSettings()` returns all 5 settings in a single evaluate call **APIs:** | API | Description | | ------------------------------- | ------------------------ | | `flpSettings.getLanguage()` | User language code | | `flpSettings.getDateFormat()` | Date format identifier | | `flpSettings.getTimeFormat()` | Time format identifier | | `flpSettings.getTimezone()` | Timezone identifier | | `flpSettings.getNumberFormat()` | Number format identifier | | `flpSettings.getAllSettings()` | All settings in one call | **Failures:** - UShell not available — page is not an FLP application - Container not loaded — called before FLP shell bootstrap :::tip Agent Hint Use when user needs to read or verify FLP user locale settings. Do not use for changing settings — this is read-only. ::: --- ## DX: Developer Experience & Tooling ### UI5-DX-001 — Test data generation Generate test data from templates with UUID/timestamp placeholders and auto-cleanup. **Guarantees:** - `{{uuid}}` generates unique value per occurrence - `{{timestamp}}` generates ISO-8601 at generation time - Recursive template substitution for nested objects/arrays - Auto-cleanup removes all persisted files on test teardown **APIs:** | API | Description | | ------------------------------- | ------------------------------------------- | | `testData.generate(template)` | Substitute placeholders in template | | `testData.save(filename, data)` | Persist as JSON file | | `testData.load(filename)` | Load and parse JSON file | | `testData.cleanup()` | Remove all tracked files (auto on teardown) | **Failures:** - File write permission — test output directory may not be writable - JSON parse error — corrupted file on load :::tip Agent Hint Use when user needs to generate unique test data or persist data between test steps. Do not use for production data — this is ephemeral test data only. ::: ### UI5-DX-002 — CLI tooling Scaffold projects, diagnose environments, and manage plugin installation. **Guarantees:** - No external CLI framework — minimal Node.js argv parsing - Cross-platform compatible (Windows/macOS/Linux) **APIs:** | API | Description | | --------------------------------- | --------------------------- | | `npx playwright-praman init` | Scaffold new Praman project | | `npx playwright-praman doctor` | Run environment diagnostics | | `npx playwright-praman uninstall` | Remove scaffolded files | | `npx playwright-praman --version` | Print package version | **Failures:** - Wrong Node.js version — requires `>=22` - Missing Playwright — init fails without `@playwright/test` peer :::tip Agent Hint Use when user needs to set up a new Praman project or diagnose environment issues. Do not use for test execution — use `npx playwright test` instead. ::: ### UI5-DX-003 — Compliance and OData trace reporters Generate structured compliance reports and OData trace logs from test runs. **Guarantees:** - `ComplianceReporter` tracks Praman step usage per test - `ODataTraceReporter` captures all OData requests with entity stats - Both implement Playwright's `Reporter` interface for config integration **APIs:** | API | Description | | -------------------- | --------------------------------------------- | | `ComplianceReporter` | Tracks `isPramanStep()` usage per test | | `ODataTraceReporter` | Captures OData HTTP requests and entity stats | **Failures:** - Reporter not configured — must be added to `playwright.config.ts` - Output directory missing — reporter creates output dir if needed :::tip Agent Hint Use when user needs compliance reports or OData request traces from test runs. Do not use for test assertions — these are post-run reporting tools. ::: ### UI5-DX-004 — Structured error hierarchy Rich error classes with error codes, suggestions, and retry hints for debugging. **Guarantees:** - All errors extend `PramanError` with `code`, `attempted`, `retryable`, `suggestions[]` - 14 error classes covering every failure domain (control, auth, nav, OData, etc.) - Machine-readable `ErrorCode` enum for programmatic error handling **APIs:** | API | Description | | ----------------- | ------------------------------------------- | | `PramanError` | Base class with structured fields | | `ControlError` | Control not found / interaction failed | | `AuthError` | Authentication flow failures | | `NavigationError` | FLP navigation failures | | `BridgeError` | Browser bridge injection/execution failures | | `ConfigError` | Configuration validation failures | | `TimeoutError` | Operation timeout with `timeoutMs` field | | `ODataError` | OData service/model errors | | `FLPError` | FLP platform service errors | | `SelectorError` | Invalid selector construction | | `AIError` | AI/LLM service errors | | `IntentError` | SAP business intent failures | | `VocabularyError` | Vocabulary resolution failures | | `PluginError` | Plugin lifecycle errors | **Failures:** - Untyped catch — always check `instanceof` for proper error handling :::tip Agent Hint Use when handling or diagnosing errors from any Praman operation. Do not use to throw custom errors — use the appropriate error subclass. ::: --- ## MIG: Migration & Compatibility ### UI5-MIG-001 — Branded types and type safety Opaque branded types prevent mixing incompatible string identifiers. **Guarantees:** - `AppId`, `ControlId`, `ViewName`, `BindingPath`, `CSSSelector` are distinct types - Constructor functions validate and brand at creation time - TypeScript compiler prevents passing `AppId` where `ControlId` is expected **APIs:** | API | Description | | ------------------ | ------------------------------- | | `appId(raw)` | Brand a string as `AppId` | | `controlId(raw)` | Brand a string as `ControlId` | | `viewName(raw)` | Brand a string as `ViewName` | | `bindingPath(raw)` | Brand a string as `BindingPath` | | `cssSelector(raw)` | Brand a string as `CSSSelector` | **Failures:** - Raw string passed — TypeScript error at compile time (not runtime) :::tip Agent Hint Use when constructing typed identifiers for Praman API calls. Do not use for runtime validation — branded types are compile-time only. ::: --- ## Roadmap (Not Yet Shipped) :::caution Planned — Not Available Yet These capabilities are on the roadmap and not yet available in the current release. ::: | ID | Name | Target | | ------------ | ------------------------------------------------------------------------------------ | ------ | | UI5-ROAD-001 | Web Components adapter — Shadow DOM traversal for SAP UI5 Web Components hybrid apps | v1.1.0 | | UI5-ROAD-002 | Visual regression testing — Screenshot comparison with UI5-aware masking | v1.2.0 | | UI5-ROAD-003 | Record and replay — Record user interactions and generate Praman test code | v1.2.0 | --- ## Appendix A: API Cross-Reference {#appendix-a} Alphabetical list of every public API mapped to its capability ID. AI agents use this to map a method name to its behavioral context.
Full API → Capability mapping (140+ entries) | API | Capability | | -------------------------- | ------------- | | `AgenticHandler` | UI5-AI-003 | | `AIError` | UI5-DX-004 | | `APIAuthStrategy` | UI5-AUTH-001 | | `appId` | UI5-MIG-001 | | `assertField` | UI5-INT-001 | | `AuthError` | UI5-DX-004 | | `bindingPath` | UI5-MIG-001 | | `BridgeError` | UI5-DX-004 | | `buildPageContext` | UI5-AI-001 | | `callFunctionImport` | UI5-DATA-002 | | `capabilities` | UI5-AI-002 | | `CapabilityRegistry` | UI5-AI-002 | | `CertificateAuthStrategy` | UI5-AUTH-001 | | `checkDeliveryStatus` | UI5-INT-001 | | `clearCustomStrategies` | UI5-AUTH-001 | | `clearFilterBar` | UI5-FE-001 | | `clickButton` | UI5-INT-001 | | `clickRow` | UI5-TBL-001 | | `clickTableSettingsButton` | UI5-TBL-001 | | `CloudSAMLAuthStrategy` | UI5-AUTH-001 | | `ComplianceReporter` | UI5-DX-003 | | `ConfigError` | UI5-DX-004 | | `confirmAndWait` | UI5-INT-001 | | `confirmDialog` | UI5-DLG-001 | | `ControlError` | UI5-DX-004 | | `controlId` | UI5-MIG-001 | | `createAuthStrategy` | UI5-AUTH-001 | | `createEntity` | UI5-DATA-002 | | `createLlmService` | UI5-AI-003 | | `createVocabularyService` | UI5-VOCAB-001 | | `createWorkZoneManager` | UI5-NAV-001 | | `cssSelector` | UI5-MIG-001 | | `DATE_FORMATS` | UI5-DTP-001 | | `DEFAULT_TIMEOUTS` | UI5-WAIT-001 | | `defineConfig` | UI5-FIX-002 | | `deleteEntity` | UI5-DATA-002 | | `deselectAllTableRows` | UI5-TBL-001 | | `detectSystemType` | UI5-AUTH-001 | | `detectTableType` | UI5-TBL-001 | | `discoverPage` | UI5-AI-001 | | `dismissDialog` | UI5-DLG-001 | | `DomFirstStrategy` | UI5-ACT-001 | | `ensureRowVisible` | UI5-TBL-001 | | `ErrorCode` | UI5-DX-004 | | `executeSearch` | UI5-FE-001 | | `exportTableData` | UI5-TBL-001 | | `feClickListItem` | UI5-FE-003 | | `feClickRow` | UI5-FE-003 | | `feFindListItemByTitle` | UI5-FE-003 | | `feFindRowByValues` | UI5-FE-003 | | `feGetCellValue` | UI5-FE-003 | | `feGetColumnNames` | UI5-FE-003 | | `feGetListItemCount` | UI5-FE-003 | | `feGetListItemTitle` | UI5-FE-003 | | `feGetTableRowCount` | UI5-FE-003 | | `feSelectListItem` | UI5-FE-003 | | `FETestLibraryInstance` | UI5-FE-001 | | `fetchCSRFToken` | UI5-DATA-001 | | `fillField` | UI5-INT-001 | | `filterByColumn` | UI5-TBL-001 | | `findRowByValues` | UI5-TBL-001 | | `FLPError` | UI5-DX-004 | | `formatDateForUI5` | UI5-DTP-001 | | `getAvailableVariants` | UI5-FE-001 | | `getCellByColumnName` | UI5-TBL-001 | | `getColumnNames` | UI5-TBL-001 | | `getCurrentHash` | UI5-NAV-001 | | `getDatePickerValue` | UI5-DTP-001 | | `getDateRangeSelection` | UI5-DTP-001 | | `getDialogButtons` | UI5-DLG-001 | | `getEntityCount` | UI5-DATA-001 | | `getFilterBar` | UI5-FE-001 | | `getFilterBarFieldValue` | UI5-FE-001 | | `getFilterValue` | UI5-TBL-001 | | `getHeaderTitle` | UI5-FE-002 | | `getListReportTable` | UI5-FE-001 | | `getModelData` | UI5-DATA-001 | | `getModelProperty` | UI5-DATA-001 | | `getObjectPageSections` | UI5-FE-002 | | `getOpenDialogs` | UI5-DLG-001 | | `getRowCount` | UI5-TBL-001 | | `getSectionData` | UI5-FE-002 | | `getSelectedRows` | UI5-TBL-001 | | `getSortOrder` | UI5-TBL-001 | | `getTableCellValue` | UI5-TBL-001 | | `getTableData` | UI5-TBL-001 | | `getTableRowCount` | UI5-TBL-001 | | `getTableRows` | UI5-TBL-001 | | `getTimePickerValue` | UI5-DTP-001 | | `hasPendingChanges` | UI5-DATA-001 | | `initializeFETestLibrary` | UI5-FE-001 | | `IntentError` | UI5-DX-004 | | `isAuthenticated` | UI5-AUTH-002 | | `isDialogOpen` | UI5-DLG-001 | | `isInEditMode` | UI5-FE-002 | | `isLoginPageVisible` | UI5-AUTH-002 | | `isShellVisible` | UI5-AUTH-002 | | `isUI5Loaded` | UI5-AUTH-002 | | `isUserMenuVisible` | UI5-AUTH-002 | | `loadConfig` | UI5-FIX-002 | | `MultiTenantAuthStrategy` | UI5-AUTH-001 | | `navigateBack` | UI5-NAV-001 | | `navigateForward` | UI5-NAV-001 | | `navigateToApp` | UI5-NAV-001 | | `navigateToHash` | UI5-NAV-001 | | `navigateToHome` | UI5-NAV-001 | | `navigateToIntent` | UI5-NAV-001 | | `navigateToItem` | UI5-FE-001 | | `navigateToSection` | UI5-FE-002 | | `navigateToTile` | UI5-NAV-001 | | `NavigationError` | UI5-DX-004 | | `ODataError` | UI5-DX-004 | | `ODataTraceReporter` | UI5-DX-003 | | `Office365AuthStrategy` | UI5-AUTH-001 | | `OnPremAuthStrategy` | UI5-AUTH-001 | | `Opa5Strategy` | UI5-ACT-001 | | `PACKAGE_NAME` | UI5-FIX-001 | | `PluginError` | UI5-DX-004 | | `PramanError` | UI5-DX-004 | | `queryEntities` | UI5-DATA-002 | | `RecipeRegistry` | UI5-AI-002 | | `recipes` | UI5-AI-002 | | `registerAuthStrategy` | UI5-AUTH-001 | | `retry` | UI5-WAIT-001 | | `SAPAuthHandler` | UI5-AUTH-001 | | `searchAndOpenApp` | UI5-NAV-001 | | `selectAllTableRows` | UI5-TBL-001 | | `selectOption` | UI5-INT-001 | | `selectRowByValues` | UI5-TBL-001 | | `selectTableRow` | UI5-TBL-001 | | `selectVariant` | UI5-FE-001 | | `SelectorError` | UI5-DX-004 | | `setAndValidateDate` | UI5-DTP-001 | | `setDatePickerValue` | UI5-DTP-001 | | `setDateRangeSelection` | UI5-DTP-001 | | `setFilterBarField` | UI5-FE-001 | | `setTableCellValue` | UI5-TBL-001 | | `setTimePickerValue` | UI5-DTP-001 | | `sortByColumn` | UI5-TBL-001 | | `TimeoutError` | UI5-DX-004 | | `UI5NativeStrategy` | UI5-ACT-001 | | `updateEntity` | UI5-DATA-002 | | `VERSION` | UI5-FIX-001 | | `viewName` | UI5-MIG-001 | | `VocabularyError` | UI5-DX-004 | | `waitForDialog` | UI5-DLG-001 | | `waitForDialogClosed` | UI5-DLG-001 | | `waitForLoad` | UI5-DATA-001 | | `waitForSave` | UI5-INT-001 | | `waitForTableData` | UI5-TBL-001 | | `waitForUI5Bootstrap` | UI5-WAIT-001 | | `waitForUI5Stable` | UI5-WAIT-001 |
--- ## Appendix B: Capability → SKILL.md Mapping {#appendix-b} Maps each capability to its corresponding SKILL.md section. AI agents use this for progressive disclosure — load only the skill section relevant to the current task.
Full Capability → SKILL.md mapping (36 entries) | Capability | SKILL.md Section | When to Load | | ------------- | ------------------------------------ | -------------------------------------------------------------------------- | | UI5-LOC-001 | `## Control Discovery` | User mentions locating, finding, or selecting UI5 controls | | UI5-LOC-002 | `## Selector Engine` | User mentions `ui5=` selector prefix or Playwright locator integration | | UI5-LOC-003 | `## Control Inspection` | User mentions inspecting, debugging, or reading control metadata | | UI5-ACT-001 | `## Interactions` | User mentions clicking, typing, filling, selecting, or checking controls | | UI5-ACT-002 | `## Reading Values` | User mentions reading text or values from UI5 controls | | UI5-ACT-003 | `## Footer Actions` | User mentions Save, Edit, Cancel, Delete, or Apply buttons | | UI5-WAIT-001 | `## Synchronization` | User mentions waiting, stability, or async rendering issues | | UI5-WAIT-002 | `## Request Interception` | User mentions WalkMe, analytics interference, or request blocking | | UI5-ASRT-001 | `## Assertions` | User mentions asserting or verifying UI5 control properties | | UI5-ASRT-002 | `## Table Assertions` | User mentions asserting table row count, cell text, or selections | | UI5-NAV-001 | `## Navigation` | User mentions navigating to Fiori apps, tiles, or FLP home | | UI5-NAV-002 | `## Shell Header` | User mentions shell header, home button, user menu, or notifications | | UI5-AUTH-001 | `## Authentication` | User mentions login, logout, SAP auth, or session management | | UI5-AUTH-002 | `## Auth State Checks` | User mentions checking if page is authenticated or login page visible | | UI5-DATA-001 | `## OData Model` | User mentions reading OData model data, properties, or pending changes | | UI5-DATA-002 | `## OData HTTP` | User mentions creating, updating, deleting, or querying OData entities | | UI5-TBL-001 | `## Table Operations` | User mentions tables, rows, cells, filtering, sorting, or exporting | | UI5-DLG-001 | `## Dialogs` | User mentions dialogs, popovers, message boxes, or overlays | | UI5-DTP-001 | `## Date Time` | User mentions date pickers, time pickers, or date range selection | | UI5-FE-001 | `## Fiori Elements List Report` | User mentions List Report, filter bar, variants, or FE search | | UI5-FE-002 | `## Fiori Elements Object Page` | User mentions Object Page, sections, edit mode, or FE header | | UI5-FE-003 | `## Fiori Elements Tables and Lists` | User mentions FE-specific tables or lists | | UI5-FIX-001 | `## Fixtures` | User asks about importing or composing Praman fixtures | | UI5-FIX-002 | `## Configuration` | User asks about configuring Praman settings or timeouts | | UI5-AI-001 | `## AI Discovery` | User mentions AI page discovery or building page context for LLMs | | UI5-AI-002 | `## AI Registries` | User mentions capability registry, recipe registry, or AI tool selection | | UI5-AI-003 | `## AI LLM Integration` | User mentions LLM providers, agentic test generation, or AI workflows | | UI5-VOCAB-001 | `## Vocabulary` | User mentions business terms, synonym resolution, or vocabulary lookup | | UI5-INT-001 | `## SAP Business Intents` | User mentions purchase orders, sales orders, invoices, or SAP transactions | | UI5-FLP-001 | `## FLP Locks` | User mentions SM12 lock entries or test isolation locks | | UI5-FLP-002 | `## FLP Settings` | User mentions FLP user settings, language, timezone, or date format | | UI5-DX-001 | `## Test Data` | User mentions generating test data, UUID placeholders, or data persistence | | UI5-DX-002 | `## CLI` | User mentions project setup, init, doctor, or uninstall commands | | UI5-DX-003 | `## Reporters` | User mentions compliance reports, OData traces, or test run analytics | | UI5-DX-004 | `## Error Handling` | User encounters an error or asks about error codes and handling | | UI5-MIG-001 | `## Type Safety` | User asks about branded types, AppId, ControlId, or type-safe strings |
--- ## Appendix C: Failure → Resolution Quick Reference {#appendix-c} For each common failure, the resolution and associated capability. AI agents use this for self-healing. | Failure | Capability | Resolution | | -------------------------------------------- | ------------- | -------------------------------------------------------------------------------------------------------------- | | Control not rendered yet | UI5-LOC-001 | Add `waitForUI5Bootstrap` before control discovery, or increase timeout | | Ambiguous selector matches multiple controls | UI5-LOC-001 | Add `viewName`, `controlType`, or `ancestor` constraint to narrow scope | | Timeout waiting for control | UI5-LOC-001 | Increase timeout, verify navigation completed, check if control is in a dialog (add `searchOpenDialogs: true`) | | Bridge not injected | UI5-WAIT-001 | Bridge auto-injects on first use; if page navigated, injection resets automatically | | WalkMe interfering with tests | UI5-WAIT-002 | Verify stability fixture is active; add WalkMe URL pattern to `ignoreAutoWaitUrls` config | | Dialog button not found | UI5-DLG-001 | Check button text matches exactly; use `getButtons()` to list available buttons first | | Table has no rows | UI5-TBL-001 | Use `waitForData()` or `waitForLoad()` before reading table; verify filter criteria | | OData model path has no data | UI5-DATA-001 | Verify the path matches bound entities; use `waitForLoad()` after navigation | | CSRF token expired | UI5-DATA-002 | Fetch a new token via `fetchCSRFToken()` before the write operation | | Authentication failed | UI5-AUTH-001 | Verify credentials; check if strategy matches system type (on-prem vs cloud) | | SM12_SRV service unavailable | UI5-FLP-001 | Activate the SM12_SRV OData service in SAP (transaction `/IWFND/MAINT_SERVICE`) | | UShell Container not available | UI5-FLP-002 | Ensure page is a Fiori Launchpad app and shell is fully loaded | | Intent navigation wrong app | UI5-INT-001 | Verify semantic object hash mapping; check FLP catalog configuration | | Vocabulary term not found | UI5-VOCAB-001 | Load the correct domain via `loadDomain()` before searching; check for typos | | Date format mismatch | UI5-DTP-001 | Use `formatDateForUI5()` to convert dates; check FLP user date format settings | | Footer button not found | UI5-ACT-003 | Verify the page is in the correct mode (edit vs display); check button text exactly |