Prueba de páginas personalizadas en aplicaciones controladas por modelos

Las páginas personalizadas son aplicaciones Canvas insertadas dentro de una aplicación basada en modelos. Se representan en un iframe dentro del shell de la aplicación controlada por modelos. Para probarlas, es necesario navegar a la aplicación controlada por modelos, seleccionar la página personalizada del mapa del sitio y, a continuación, determinar el ámbito de todas las interacciones de control en el iframe interno.

Funcionamiento de las pruebas de páginas personalizadas en aplicaciones controladas por modelos

Cuando se carga una página personalizada, el shell de aplicación controlado por modelos permanece en el dominio Dynamics 365. El tiempo de ejecución del lienzo de página personalizado se carga dentro de:

iframe[name="fullscreen-app-host"]

Este es el mismo iframe que usan las aplicaciones canvas independientes. Una vez que tenga el localizador, se aplicarán todos los patrones de pruebas para aplicaciones de canvas.

  1. Inicie la aplicación controlada por modelos mediante AppProvider.
  2. Seleccione el elemento de página personalizado en el mapa del sitio.
  3. Espere a que el runtime del canvas se inicialice.
import { test, expect } from '@playwright/test';
import { AppProvider, AppType, AppLaunchMode } from 'power-platform-playwright-toolkit';

const MODEL_DRIVEN_APP_URL = process.env.MODEL_DRIVEN_APP_URL!;
const CUSTOM_PAGE_NAME = process.env.CUSTOM_PAGE_NAME ?? 'AccountsCustomPage';

test.beforeAll(async ({ browser }) => {
  const context = await browser.newContext({ storageState: mdaStorageStatePath });
  const page = await context.newPage();

  const app = new AppProvider(page, context);
  await app.launch({
    app: 'My App',
    type: AppType.ModelDriven,
    mode: AppLaunchMode.Play,
    skipMakerPortal: true,
    directUrl: MODEL_DRIVEN_APP_URL,
  });

  // Navigate to the custom page via the sitemap
  const sidebarItem = page
    .locator(`[role="presentation"][title="${CUSTOM_PAGE_NAME}"]`)
    .first();
  await sidebarItem.waitFor({ state: 'visible', timeout: 30000 });
  await sidebarItem.click();
  await page.waitForTimeout(3000);
});

Interacción con controles de página personalizados

Después de navegar a la página personalizada, limite los localizadores al iframe del lienzo.

const canvasFrame = page.frameLocator('iframe[name="fullscreen-app-host"]');

// Wait for gallery to appear
await canvasFrame
  .locator('[data-control-part="gallery-item"]')
  .first()
  .waitFor({ state: 'visible', timeout: 30000 });

Botones de clic en controles de página personalizados

Use el data-control-name atributo para dirigir controles de botón específicos dentro del iframe de lienzo y, a continuación, busque el elemento interno [role="button"] para desencadenar una acción de clic.

await canvasFrame.locator('[data-control-name="IconButton_Accept1"] [role="button"]').click();
await canvasFrame.locator('[data-control-name="IconButton_Edit1"] [role="button"]').click();

Rellenar campos de formulario en una página personalizada

Busque los campos de entrada por su aria-label atributo dentro del iframe de lienzo y use el fill método para escribir valores.

const accountNameInput = canvasFrame.locator('input[aria-label="Account Name"]');
await accountNameInput.fill('Contoso Ltd');

Para buscar un elemento específico en una galería, filtre la lista de elementos de la galería haciendo coincidir el contenido de texto de un control secundario, como Title1.

const galleryItem = canvasFrame
  .locator('[role="listitem"][data-control-part="gallery-item"]')
  .filter({
    has: canvasFrame
      .locator('[data-control-name="Title1"]')
      .getByText('Contoso Ltd', { exact: true }),
  });

await galleryItem.waitFor({ state: 'visible', timeout: 30000 });

Actualizar la página personalizada después de guardar

Al guardar un nuevo registro en una página personalizada respaldada por Dataverse, la galería no se actualiza automáticamente a menos que desencadene una recarga completa. El enfoque recomendado es ir a la raíz de la aplicación y volver a:

// Navigate to app root to force gallery refresh
await page.goto(MODEL_DRIVEN_APP_URL, { waitUntil: 'load', timeout: 60000 });
await page.locator('[role="menuitem"]').first().waitFor({ state: 'visible', timeout: 30000 });

// Navigate back to the custom page
const sidebarItem = page.locator(`[role="presentation"][title="${CUSTOM_PAGE_NAME}"]`).first();
await sidebarItem.waitFor({ state: 'visible', timeout: 30000 });
await sidebarItem.click();

// Wait for the new record to appear in the gallery
const specificItem = page
  .locator('[data-control-part="gallery-item"]')
  .filter({ has: page.locator('[data-control-name="Title1"]').getByText(accountName, { exact: true }) });
await specificItem.waitFor({ state: 'visible', timeout: 60000 });

Ejemplo de prueba completo: creación y comprobación de un registro

En el ejemplo siguiente se combina la navegación, la entrada de formulario, el guardado y la comprobación de la galería en una única prueba de un extremo a otro que crea un registro de cuenta y confirma que aparece en la galería de páginas personalizada.

test('should create an account and verify it in the gallery', async ({ page }) => {
  const ACCOUNT_NAME = `Test Account ${Date.now()}`;
  const canvasFrame = page.frameLocator('iframe[name="fullscreen-app-host"]');

  // Click New record
  await page.locator('[title="New record"]').click();

  // Fill the form
  await canvasFrame.locator('input[aria-label="Account Name"]').fill(ACCOUNT_NAME);
  await canvasFrame.locator('input[aria-label="Main Phone"]').fill('555-1234');

  // Save
  await canvasFrame
    .locator('[data-control-name="IconButton_Accept1"] [role="button"]')
    .click();

  // Refresh and verify
  await page.waitForTimeout(5000); // wait for Dataverse write
  await page.goto(MODEL_DRIVEN_APP_URL, { waitUntil: 'load', timeout: 60000 });
  await page.locator('[role="menuitem"]').first().waitFor({ timeout: 30000 });
  await page.locator(`[role="presentation"][title="${CUSTOM_PAGE_NAME}"]`).first().click();

  await expect(
    page
      .locator('[data-control-part="gallery-item"]')
      .filter({ has: page.locator('[data-control-name="Title1"]').getByText(ACCOUNT_NAME, { exact: true }) })
  ).toBeVisible({ timeout: 60000 });
});

Autenticación para páginas personalizadas

Las páginas personalizadas se ejecutan en el dominio de Dynamics 365. Use el estado de almacenamiento de aplicaciones controlado por modelos:

test.use({
  storageState: path.join(
    path.dirname(getStorageStatePath(process.env.MS_AUTH_EMAIL!)),
    `state-mda-${process.env.MS_AUTH_EMAIL}.json`
  ),
});

Pasos siguientes

Consulte también