Probar aplicaciones controladas por modelos

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);

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();
});

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.

Pasos siguientes

Consulte también