Nota:
El acceso a esta página requiere autorización. Puede intentar iniciar sesión o cambiar directorios.
El acceso a esta página requiere autorización. Puede intentar cambiar los directorios.
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 maneja el AG Grid que impulsa las vistas de lista impulsadas por modelos. Usa [row-index] atributos para destinos de fila confiables 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 | Usar [row-index] selector (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. |