Hinweis
Für den Zugriff auf diese Seite ist eine Autorisierung erforderlich. Sie können versuchen, sich anzumelden oder das Verzeichnis zu wechseln.
Für den Zugriff auf diese Seite ist eine Autorisierung erforderlich. Sie können versuchen, das Verzeichnis zu wechseln.
Modellgesteuerte Apps rendern Listenansichten unter Verwendung einer Datenrasterkomponente und einer Formularlaufzeitumgebung zur Datensatzbearbeitung. Die GridComponent und FormComponent Klassen des Frameworks abstrahieren die zugrunde liegende DOM-Struktur, sodass Sie keine komplexen Selektoren schreiben müssen. In diesem Leitfaden wird gezeigt, wie Sie zu einer App navigieren, mit dem Raster interagieren, Datensätze öffnen und Formularfeldwerte mithilfe der ModelDrivenAppPage integrierten Komponenten überprüfen.
Starten einer modellgesteuerten App
Verwenden Sie
import { test } from '@playwright/test';
import { AppProvider, AppType, AppLaunchMode } from 'power-platform-playwright-toolkit';
const MODEL_DRIVEN_APP_URL = process.env.MODEL_DRIVEN_APP_URL!;
test.beforeEach(async ({ page, context }) => {
const app = new AppProvider(page, context);
await app.launch({
app: 'Northwind Orders',
type: AppType.ModelDriven,
mode: AppLaunchMode.Play,
skipMakerPortal: true,
directUrl: MODEL_DRIVEN_APP_URL,
});
modelDrivenApp = app.getModelDrivenAppPage();
});
Arbeiten mit dem Raster
Das GridComponent verarbeitet das AG Grid, das modellgesteuerte Listenansichten unterstützt. Es verwendet [row-index] Attribute für eine zuverlässige Zeilenadressierung, die nach dem Filtern ordnungsgemäß funktioniert.
Warten, bis das Raster geladen wird
Navigieren Sie zur Listenansicht einer Entität, und warten Sie, bis die Rasterzeilen gerendert werden, bevor sie mit ihnen interagieren.
await modelDrivenApp.navigateToGridView('nwind_orders');
await modelDrivenApp.grid.waitForGridLoad();
Raster filtern
Schränken Sie die Rasterergebnisse ein, indem Sie nach allen sichtbaren Spalten suchen oder auf eine bestimmte Spalte abzielen.
// Filter by keyword (searches across visible columns)
await modelDrivenApp.grid.filterByKeyword('ORD-12345');
await modelDrivenApp.grid.waitForGridLoad();
// Filter by a specific column
await modelDrivenApp.grid.filterByColumn('Order', 'ORD-12345');
Lesen von Zellwerten
Rufen Sie den angezeigten Wert einer Zelle nach Zeilenindex und Spaltenname (Schemaname oder Anzeigename) ab.
// By column schema name
const orderNumber = await modelDrivenApp.grid.getCellValue(0, 'nwind_ordernumber');
// By column display name
const status = await modelDrivenApp.grid.getCellValue(0, 'Order Status');
Öffnen Sie einen Datensatz
Öffnen Sie das Formular eines Datensatzes anhand der Zeilennummer oder indem Sie einen Spaltenwert abgleichen.
// Open the first record in the grid
await modelDrivenApp.grid.openRecord({ rowNumber: 0 });
// Open a record by column value
await modelDrivenApp.grid.openRecord({
columnName: 'Order Number',
columnValue: 'ORD-12345',
});
Zeilen auswählen
Wählen Sie eine oder mehrere Zeilen im Raster aus, oder überprüfen Sie, ob das Raster Datensätze enthält.
// Select one row
await modelDrivenApp.grid.selectRow(0);
// Select multiple rows
await modelDrivenApp.grid.selectRows([0, 1, 2]);
// Check if grid is empty
const isEmpty = await modelDrivenApp.grid.isGridEmpty();
Mit Formularen arbeiten
Der FormComponent umschließt die Dynamics 365 Formularlaufzeit und die Xrm FormContext-API.
Lesen von Feldwerten
Verwenden Sie getAttribute() um den aktuellen Wert eines Formularfelds anhand des Schemanamens abzurufen.
const orderNumber = await modelDrivenApp.form.getAttribute('nwind_ordernumber');
const status = await modelDrivenApp.form.getAttribute('nwind_orderstatusid');
Feldwerte schreiben
Wird setAttribute() verwendet, um den Wert eines Felds programmgesteuert im Formular festzulegen.
await modelDrivenApp.form.setAttribute('nwind_ordernumber', 'ORD-99999');
await modelDrivenApp.form.setAttribute('nwind_notes', 'Updated via test');
Speichern des Formulars
Speichern Sie den Datensatz, und überprüfen Sie, ob das Formular nicht mehr schmutzig ist und die Überprüfung besteht.
await modelDrivenApp.form.save();
// Verify the form saved successfully
expect(await modelDrivenApp.form.isDirty()).toBe(false);
expect(await modelDrivenApp.form.isValid()).toBe(true);
Navigieren in Formularregisterkarten
Wechseln Sie zwischen den Registerkarten im Formular, um auf Felder in verschiedenen Abschnitten zuzugreifen.
await modelDrivenApp.form.navigateToTab('Summary');
await modelDrivenApp.form.navigateToTab('Details');
Steuerfeld Sichtbarkeit und Zustand
Ändern der Sichtbarkeit, des deaktivierten Zustands oder der erforderlichen Ebene eines Felds zur Laufzeit.
await modelDrivenApp.form.setFieldVisibility('nwind_notes', true);
await modelDrivenApp.form.setFieldDisabled('nwind_ordernumber', false);
await modelDrivenApp.form.setFieldRequiredLevel('nwind_customerid', 'required');
Ausführen von benutzerdefiniertem Xrm-Code
Führen Sie Code, der die Dynamics 365 Xrm-API direkt im Formularkontext nutzt, aus:
const result = await modelDrivenApp.form.execute(async (formContext) => {
const attr = formContext.getAttribute('nwind_ordernumber');
return attr?.getValue();
});
console.log(`Order number from Xrm: ${result}`);
Vollständiges CRUD-Workflowbeispiel
Dieser Test veranschaulicht einen vollständigen Erstellungs-, Lese-, Aktualisierungs- und Löschworkflow für eine modellgesteuerte App.
import { test, expect } from '@playwright/test';
import { AppProvider, AppType, AppLaunchMode } from 'power-platform-playwright-toolkit';
const ENTITY = 'nwind_orders';
const APP_URL = process.env.MODEL_DRIVEN_APP_URL!;
test('should create, read, update, and delete an order', async ({ page, context }) => {
const app = new AppProvider(page, context);
await app.launch({
app: 'Northwind Orders',
type: AppType.ModelDriven,
mode: AppLaunchMode.Play,
skipMakerPortal: true,
directUrl: APP_URL,
});
const mda = app.getModelDrivenAppPage();
// Step 1: Create a new order record
await mda.navigateToFormView(ENTITY);
await page.locator('input[data-id="nwind_ordernumber.fieldControl-text-box-text"]').fill('ORD-TEST-001');
await page.locator('button[aria-label*="Save"]').first().click();
await page.waitForTimeout(3000);
// Step 2: Read the record in the grid
await mda.navigateToGridView(ENTITY);
await mda.grid.waitForGridLoad();
await mda.grid.filterByKeyword('ORD-TEST-001');
await mda.grid.waitForGridLoad();
expect(await mda.grid.getRowCount()).toBeGreaterThan(0);
const cellValue = await mda.grid.getCellValue(0, 'Order Number');
expect(cellValue).toContain('ORD-TEST-001');
// Step 3: Update the order number
await mda.grid.openRecord({ rowNumber: 0 });
await page.locator('input[data-id="nwind_ordernumber.fieldControl-text-box-text"]').fill('ORD-TEST-001-UPDATED');
await page.locator('button[aria-label*="Save"]').first().click();
await page.waitForTimeout(3000);
// Step 4: Delete the record
await mda.navigateToGridView(ENTITY);
await mda.grid.waitForGridLoad();
await mda.grid.filterByKeyword('ORD-TEST-001-UPDATED');
await mda.grid.waitForGridLoad();
await mda.grid.selectRow(0);
await page.locator('button[aria-label*="Delete"]').first().click();
// Confirm the deletion dialog
const dialog = page.locator('[role="dialog"]');
await dialog.locator('button:has-text("Delete")').click();
});
Navigieren mit der Sitemap
Verwenden Sie die Sitemap-Randleiste, um zu einer bestimmten Entitätslistenansicht zu navigieren.
// Navigate to a specific entity list view via sitemap
const sidebarItem = page.locator('[role="presentation"][title="Orders"]').first();
await sidebarItem.waitFor({ state: 'visible', timeout: 15000 });
await sidebarItem.click();
Lösen von Gitterproblemen
In der folgenden Tabelle sind allgemeine Probleme mit der Rasterinteraktion und deren Behebung aufgeführt.
| Symptom | Wahrscheinliche Ursache | Resolution |
|---|---|---|
| Zeile nach dem Filtern nicht gefunden | Raster wird weiterhin neu gerendert |
await mda.grid.waitForGridLoad() nach dem Filter hinzufügen |
nth-child Selektor schlägt fehl |
AG Grid-Kopfzeilen-/Zeilenstruktur | Verwenden Sie den [row-index] Selektor (integriert in GridComponent) |
| Kontrollkästchen durch Überlagerung blockiert | Überlagerungen des Häkchensymbols auf Eingabefeldern |
{ force: true } beim Anklicken des Kontrollkästchens verwenden |