Skip to main content

sap-fiori-testing

SAP Fiori/UI5 testing, accessibility, and modern UI skill. Use when writing wdi5 E2E tests, OPA5 integration tests, implementing WCAG 2.1 accessibility, using UI5 Web Components, or migrating UI5 to TypeScript. If the user mentions wdi5, OPA5, Fiori test, UI5 accessibility, WCAG, or UI5 TypeScript migration, use this skill.

Zur Installation springen

Quellinformationen

Repository
efeumutaslan/SAP-SKILLS
Letzte Quellaktivität
24. März 2026 um 21:17
Erkannte Sprache von SKILL.md
Englisch
Sterne
5
Forks
1

Installationsoptionen

Standardmäßig ist der Prompt ausgewählt, der zuerst die Quelle prüft. Sie können zu einem direkten Befehl wechseln oder eine lokale Kopie herunterladen.

Quelldateien prüfen

Lesen Sie SKILL.md und alle von SkillsMP angezeigten Begleitdateien, bevor Sie sich für eine Installation entscheiden.

Datei-Explorer
2 Dateien

SKILL.md wird angezeigt

SKILL.md
Quellanweisungen · Schreibgeschützte Vorschau
name
sap-fiori-testing
description
SAP Fiori/UI5 testing, accessibility, and modern UI skill. Use when writing wdi5 E2E tests, OPA5 integration tests, implementing WCAG 2.1 accessibility, using UI5 Web Components, or migrating UI5 to TypeScript. If the user mentions wdi5, OPA5, Fiori test, UI5 accessibility, WCAG, or UI5 TypeScript migration, use this skill.
license
MIT
metadata
{"author":"SAP Skills Community","version":"1.0.0","last_verified":"2026-03-24"}
# SAP Fiori Testing, Accessibility & Modern UI ## Related Skills - `sap-testing-quality` — Cross-cutting SAP testing strategy - `sap-devops-cicd` — UI test automation in pipelines - `sap-build-apps` — Alternative low-code UI approach ## Quick Start **Choose your test framework:** | Framework | Level | Speed | Best For | |-----------|-------|-------|----------| | **QUnit** | Unit | Fast | Controller logic, formatters | | **OPA5** | Integration | Medium | UI interaction flows, navigation | | **wdi5** | E2E | Slow | Full app testing, cross-app | | **UIVeri5** | Visual/E2E | Slow | Visual regression, screenshots | **Quick wdi5 setup:** ```bash npm init wdi5@latest # Follow prompts: select UI5 app, choose test runner # Generates: wdio.conf.js + test structure ``` ## Core Concepts ### UI5 Test Pyramid ``` /\ / \ E2E (wdi5/UIVeri5) / \ - Full app in browser /------\ - Real backend or mock server / \ / OPA5 \ Integration / Tests \ - UI interaction + navigation /--------------\ - Mock server for data / \ / QUnit Unit Tests \ Unit / \ - Pure logic, no DOM /____________________\ ``` ### wdi5 Architecture ``` Test Script ──► wdi5 Bridge ──► UI5 Control API │ │ │ WebdriverIO Injects JS sap.ui.test.* (Selenium/ into browser control selectors Playwright) ``` ### OPA5 Key Concepts | Concept | Role | Example | |---------|------|---------| | **Page Object** | Encapsulates page interactions | `onTheMainPage.iClickCreate()` | | **Journey** | Test scenario (sequence of actions) | `opaTest("Create order", ...)` | | **Arrangement** | Setup state | `Given.iStartMyApp()` | | **Action** | User interaction | `When.onTheList.iSelectItem("001")` | | **Assertion** | Verify outcome | `Then.onTheDetail.iSeeTitle("Order 001")` | ## Common Patterns ### Pattern 1: wdi5 Test with UI5 Selectors ```javascript // test/e2e/specs/order.test.js const { wdi5 } = require('wdio-ui5-service'); describe('Order Management', () => { before(async () => { await browser.url('#/Orders'); await browser.asControl({ selector: { controlType: 'sap.m.Page', viewName: 'orders.List' } }); }); it('should display order list', async () => { const table = await browser.asControl({ selector: { controlType: 'sap.m.Table', viewName: 'orders.List', id: 'orderTable' } }); const items = await table.getItems(); expect(items.length).toBeGreaterThan(0); }); it('should create new order', async () => { // Click Create button const createBtn = await browser.asControl({ selector: { controlType: 'sap.m.Button', viewName: 'orders.List', properties: { text: 'Create' } } }); await createBtn.press(); // Fill form const customerInput = await browser.asControl({ selector: { controlType: 'sap.m.Input', viewName: 'orders.Create', id: 'customerInput' } }); await customerInput.setValue('CUST001'); const amountInput = await browser.asControl({ selector: { controlType: 'sap.m.Input', viewName: 'orders.Create', id: 'amountInput' } }); await amountInput.setValue('1500.00'); // Save const saveBtn = await browser.asControl({ selector: { controlType: 'sap.m.Button', viewName: 'orders.Create', properties: { type: 'Emphasized' } } }); await saveBtn.press(); // Verify success message const msgStrip = await browser.asControl({ selector: { controlType: 'sap.m.MessageStrip', properties: { type: 'Success' } } }); const text = await msgStrip.getText(); expect(text).toContain('created successfully'); }); }); ``` ```javascript // wdio.conf.js exports.config = { specs: ['./test/e2e/specs/**/*.test.js'], maxInstances: 1, capabilities: [{ browserName: 'chrome', 'goog:chromeOptions': { args: ['--headless', '--no-sandbox', '--disable-gpu'] } }], baseUrl: 'http://localhost:8080', services: ['ui5'], ui5: { path: { webapp: 'webapp' }, url: 'http://localhost:8080/index.html' }, framework: 'mocha', mochaOpts: { timeout: 60000 } }; ``` ### Pattern 2: OPA5 Integration Test ```javascript // test/integration/pages/OrderList.js sap.ui.define([ 'sap/ui/test/Opa5', 'sap/ui/test/actions/Press', 'sap/ui/test/actions/EnterText', 'sap/ui/test/matchers/Properties', 'sap/ui/test/matchers/AggregationLengthEquals' ], function (Opa5, Press, EnterText, Properties, AggregationLengthEquals) { 'use strict'; Opa5.createPageObjects({ onTheOrderListPage: { actions: { iSearchFor: function (sQuery) { return this.waitFor({ id: 'searchField', viewName: 'orders.List', actions: new EnterText({ text: sQuery }), errorMessage: 'Search field not found' }); }, iPressCreateButton: function () { return this.waitFor({ controlType: 'sap.m.Button', viewName: 'orders.List', matchers: new Properties({ text: 'Create' }), actions: new Press(), errorMessage: 'Create button not found' }); }, iSelectFirstItem: function () { return this.waitFor({ controlType: 'sap.m.ColumnListItem', viewName: 'orders.List', actions: new Press(), errorMessage: 'No list items found' }); } }, assertions: { iShouldSeeTheTable: function () { return this.waitFor({ id: 'orderTable', viewName: 'orders.List', success: function () { Opa5.assert.ok(true, 'Table is visible'); }, errorMessage: 'Table not found' }); }, theTableShouldHaveEntries: function (iCount) { return this.waitFor({ id: 'orderTable', viewName: 'orders.List', matchers: new AggregationLengthEquals({ name: 'items', length: iCount }), success: function () { Opa5.assert.ok(true, 'Table has ' + iCount + ' entries'); }, errorMessage: 'Table does not have ' + iCount + ' entries' }); } } } }); }); ``` ```javascript // test/integration/OrderJourney.js sap.ui.define([ 'sap/ui/test/opaQunit', './pages/OrderList' ], function (opaTest) { 'use strict'; QUnit.module('Order Management'); opaTest('Should see order list on start', function (Given, When, Then) { Given.iStartMyApp(); Then.onTheOrderListPage.iShouldSeeTheTable(); }); opaTest('Should filter orders by search', function (Given, When, Then) { When.onTheOrderListPage.iSearchFor('CUST001'); Then.onTheOrderListPage.theTableShouldHaveEntries(3); }); opaTest('Should navigate to create page', function (Given, When, Then) { When.onTheOrderListPage.iPressCreateButton(); Then.onTheCreatePage.iShouldSeeTheForm(); Then.iTeardownMyApp(); }); }); ``` ### Pattern 3: Fiori Elements OPA5 Test ```javascript // test/integration/FEListReportJourney.js sap.ui.define([ 'sap/ui/test/opaQunit', 'sap/fe/test/ListReport', 'sap/fe/test/ObjectPage' ], function (opaTest, ListReport, ObjectPage) { 'use strict'; var oListReport = new ListReport({ entitySet: 'Orders' }); var oObjectPage = new ObjectPage({ entitySet: 'Orders' }); QUnit.module('Fiori Elements - List Report'); opaTest('Table loads with data', function (Given, When, Then) { Given.iStartMyFLPApp('orders-manage'); Then.onTheListReport.iSeeTheTable(); Then.onTheListReport.iCheckRows(10); }); opaTest('Filter by status', function (Given, When, Then) { When.onTheListReport.iOpenFilterBar(); When.onTheListReport.iFilterByField('Status', 'Open'); When.onTheListReport.iExecuteFilter(); Then.onTheListReport.iCheckRows(5); }); opaTest('Navigate to detail', function (Given, When, Then) { When.onTheListReport.iPressRow(0); Then.onTheObjectPage.iSeeThisPage(); Then.onTheObjectPage.iSeeHeaderTitle('ORD-001'); Then.iTeardownMyApp(); }); }); ``` ### Pattern 4: WCAG 2.1 Accessibility Checklist ```javascript // Accessibility patterns for Fiori apps // 1. Labels for all inputs // BAD: new sap.m.Input({ placeholder: "Enter name" }); // GOOD: new sap.m.Label({ text: "Customer Name", labelFor: "nameInput" }); new sap.m.Input({ id: "nameInput" }); // 2. ARIA for custom controls new sap.m.GenericTile({ header: "Revenue", ariaLabel: "Revenue tile showing 1.5 million euros", press: function() { /* navigate */ } }); // 3. High contrast support — use semantic colors // BAD: custom CSS colors // GOOD: use UI5 semantic classes // .sapMObjStatusActive (standard active state) // sap.ui.core.IconColor.Positive / Negative / Critical // 4. Keyboard navigation // Ensure all interactive elements are reachable via Tab // Use sap.m.Table not custom HTML for data grids // Test with screen reader (JAWS, NVDA, VoiceOver) // 5. Color-independent information // BAD: status shown only by color // GOOD: status shown by color + icon + text new sap.m.ObjectStatus({ text: "Approved", state: "Success", icon: "sap-icon://accept" }); ``` ### Pattern 5: UI5 TypeScript Migration ```typescript // webapp/controller/OrderList.controller.ts import Controller from "sap/ui/core/mvc/Controller"; import JSONModel from "sap/ui/model/json/JSONModel"; import Filter from "sap/ui/model/Filter"; import FilterOperator from "sap/ui/model/FilterOperator"; import MessageToast from "sap/m/MessageToast"; import Event from "sap/ui/base/Event"; import Table from "sap/m/Table"; import ListBinding from "sap/ui/model/ListBinding"; /** * @namespace orders.controller */ export default class OrderList extends Controller { public onInit(): void { const oViewModel = new JSONModel({ busy: false, orderCount: 0 }); this.getView()?.setModel(oViewModel, "view"); } public onSearch(oEvent: Event): void { const sQuery = (oEvent.getParameter("query") as string) || ""; const aFilters: Filter[] = []; if (sQuery) { aFilters.push(new Filter("CustomerName", FilterOperator.Contains, sQuery)); }
Auf GitHub ansehen
Diese SKILL.md ist sehr gross, daher zeigt SkillsMP hier nur den ersten Abschnitt. Auf GitHub ansehen