Oharra
Baimena behar duzu orria atzitzeko. Direktorioetan saioa has dezakezu edo haiek alda ditzakezu.
Baimena behar duzu orria atzitzeko. Direktorioak alda ditzakezu.
Las aplicaciones controladas por modelos representan vistas de lista mediante un componente de cuadrícula de datos y un tiempo de ejecución de formulario para la edición de registros. Las clases GridComponent y FormComponent del framework abstraen la estructura DOM subyacente, así no necesitas escribir selectores complejos. En esta guía se muestra cómo navegar a una aplicación, interactuar con la cuadrícula, abrir registros y comprobar los valores de campo de formulario mediante ModelDrivenAppPage y sus componentes integrados.
Lanzar una aplicación basada en modelos
Use skipMakerPortal: true y directUrl para omitir Power Apps navegación y vaya directamente a la aplicación:
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();
});
Trabajar con la cuadrícula
GridComponent gestiona el AG Grid que impulsa las vistas de lista impulsadas por modelos. Usa atributos [row-index] para destinos de fila fiables que funcionan correctamente después del filtrado.
Espere a que se cargue la cuadrícula.
Vaya a la vista de lista de entidades y espere a que se rendericen las filas de la cuadrícula antes de interactuar con ellas.
await modelDrivenApp.navigateToGridView('nwind_orders');
await modelDrivenApp.grid.waitForGridLoad();
Filtrar la cuadrícula
Restrinja los resultados de la cuadrícula buscando en todas las columnas visibles o estableciendo como destino una columna específica.
// 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');
Leer valores de celda
Recupere el valor mostrado de una celda por índice de fila y nombre de columna (nombre de esquema o nombre para mostrar).
// 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');
Abrir un registro
Abra el formulario de un registro por número de fila o mediante la coincidencia de un valor de columna.
// 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',
});
Seleccionar filas
Seleccione una o varias filas en la cuadrícula o compruebe si la cuadrícula contiene registros.
// 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();
Trabajar con formularios
El FormComponent envuelve el runtime del formulario de Dynamics 365 y la API FormContext de Xrm.
Leer valores de campo
Use getAttribute() para recuperar el valor actual de un campo de formulario por su nombre de esquema.
const orderNumber = await modelDrivenApp.form.getAttribute('nwind_ordernumber');
const status = await modelDrivenApp.form.getAttribute('nwind_orderstatusid');
Escribir valores de campo
Use setAttribute() para establecer mediante programación el valor de un campo en el formulario.
await modelDrivenApp.form.setAttribute('nwind_ordernumber', 'ORD-99999');
await modelDrivenApp.form.setAttribute('nwind_notes', 'Updated via test');
Guardar el formulario
Guarde el registro y compruebe que el formulario ya no está sucio y pasa la validación.
await modelDrivenApp.form.save();
// Verify the form saved successfully
expect(await modelDrivenApp.form.isDirty()).toBe(false);
expect(await modelDrivenApp.form.isValid()).toBe(true);
Navegar por las pestañas de formulario
Cambie entre pestañas del formulario para acceder a los campos de diferentes secciones.
await modelDrivenApp.form.navigateToTab('Summary');
await modelDrivenApp.form.navigateToTab('Details');
Control de la visibilidad y el estado de los campos
Cambie la visibilidad, el estado deshabilitado o el nivel necesario de un campo en tiempo de ejecución.
await modelDrivenApp.form.setFieldVisibility('nwind_notes', true);
await modelDrivenApp.form.setFieldDisabled('nwind_ordernumber', false);
await modelDrivenApp.form.setFieldRequiredLevel('nwind_customerid', 'required');
Ejecutar código Xrm personalizado
Ejecute cualquier código que use la API de Xrm de Dynamics 365 directamente en el contexto del formulario:
const result = await modelDrivenApp.form.execute(async (formContext) => {
const attr = formContext.getAttribute('nwind_ordernumber');
return attr?.getValue();
});
console.log(`Order number from Xrm: ${result}`);
Ejemplo de flujo de trabajo CRUD completo
Esta prueba muestra un flujo de trabajo completo de creación, lectura, actualización y eliminación en una aplicación controlada por modelos.
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();
});
Navegación mediante el mapa del sitio
Use la barra lateral del mapa del sitio para navegar a una vista de lista de entidades específica.
// 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();
Resolución de problemas de red
En la tabla siguiente se enumeran los problemas comunes de interacción de cuadrícula y cómo resolverlos.
| Síntoma | Causa probable | Resolution |
|---|---|---|
| Fila no encontrada después del filtrado | La cuadrícula todavía se está reprocesando | Agregar await mda.grid.waitForGridLoad() después del filtro |
nth-child se produce un error en el selector |
Estructura de encabezado/fila de AG Grid | Use el selector [row-index] (integrado en GridComponent ) |
| Casilla de verificación bloqueada por superposición | El icono de CheckMark superpone la entrada | Usar { force: true } al hacer clic en la casilla de verificación. |