Rediger

Del via


Test AI-generated apps (Gen UX)

Gen UX apps are AI-generated Power Apps created through the natural-language experience in Power Apps. They use the same canvas runtime as hand-authored canvas apps, so the same iframe-scoping and data-control-name patterns apply. The GenUxPage class provides helpers tuned for the generated control structure.

How Gen UX apps differ from canvas apps

Gen UX apps share the canvas runtime and the iframe[name="fullscreen-app-host"] host frame. The main differences are:

  • Control names are autogenerated (for example, Button1, TextInput1) and might change when you regenerate the app.
  • The AI prompt drives the app layout, so structure varies between apps.
  • The GenUxPage class exposes helpers that work against semantic roles and ARIA labels rather than control names, making tests more resilient to regeneration.

Launch a Gen UX app

Use the AppProvider class to launch a Gen UX app in play mode, passing a direct URL to skip the maker portal and connect straight to the canvas runtime.

import { test, expect } from '@playwright/test';
import {
  AppProvider,
  AppType,
  AppLaunchMode,
  buildCanvasAppUrlFromEnv,
} from 'power-platform-playwright-toolkit';

const GEN_UX_APP_URL = buildCanvasAppUrlFromEnv();

test.beforeEach(async ({ page, context }) => {
  const app = new AppProvider(page, context);

  await app.launch({
    app: 'My AI App',
    type: AppType.Canvas,
    mode: AppLaunchMode.Play,
    skipMakerPortal: true,
    directUrl: GEN_UX_APP_URL,
  });
});

Tip

Use skipMakerPortal: true with directUrl to bypass Power Apps and reduce startup time by 10–20 seconds.

Scope locators to the canvas frame

Scope all interactions to the canvas iframe:

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

Wait for the app to load

Gen UX apps can take 30–60 seconds to load Dataverse data. Wait for a known element before interacting:

// Wait for a gallery or any visible UI element
await canvasFrame
  .locator('[data-control-part="gallery-item"]')
  .first()
  .waitFor({ state: 'visible', timeout: 60000 });

Interact with controls by ARIA label

Gen UX apps generate controls with ARIA labels derived from your prompt. Use ARIA-label selectors for resilience:

Fill a text input

Target text inputs by their aria-label attribute, which matches the Dataverse column display name, and use Playwright's fill() method to enter values.

await canvasFrame.locator('input[aria-label="Name"]').fill('Contoso Ltd');
await canvasFrame.locator('input[aria-label="Phone"]').fill('555-9000');

Click a button

Locate buttons by combining the role="button" selector with the aria-label that the AI assigned based on your prompt.

await canvasFrame.locator('[role="button"][aria-label="Save"]').click();
await canvasFrame.locator('[role="button"][aria-label="New record"]').click();

Select from a dropdown

Click the dropdown to expand its option list, then select the desired option by matching its visible text.

const dropdown = canvasFrame.locator('[aria-label="Category"]');
await dropdown.click();
await canvasFrame.locator('[role="option"]:has-text("Enterprise")').click();

Work with galleries

Gen UX galleries use the same attributes as canvas app galleries:

const gallery = canvasFrame.locator('[data-control-part="gallery-item"]');

// Count items
const count = await gallery.count();
expect(count).toBeGreaterThan(0);

// Filter by text content
const item = gallery.filter({ hasText: 'Contoso Ltd' });
await item.waitFor({ state: 'visible', timeout: 30000 });
await item.click();

Full test example: create and find a record

The following end-to-end test launches a Gen UX app, creates a new record through the form, and verifies that it appears in the gallery.

import { test, expect } from '@playwright/test';
import { AppProvider, AppType, AppLaunchMode, buildCanvasAppUrlFromEnv } from 'power-platform-playwright-toolkit';

const GEN_UX_APP_URL = buildCanvasAppUrlFromEnv();

test('should create a new record and find it in the list', async ({ page, context }) => {
  const app = new AppProvider(page, context);
  await app.launch({
    app: 'My AI App',
    type: AppType.Canvas,
    mode: AppLaunchMode.Play,
    skipMakerPortal: true,
    directUrl: GEN_UX_APP_URL,
  });

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

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

  // Click New record
  await canvasFrame.locator('[role="button"][aria-label="New record"]').click();

  // Fill the form
  const accountName = `AI Test ${Date.now()}`;
  await canvasFrame.locator('input[aria-label="Name"]').fill(accountName);
  await canvasFrame.locator('input[aria-label="Phone"]').fill('555-1234');

  // Save
  await canvasFrame.locator('[role="button"][aria-label="Save"]').click();
  await page.waitForTimeout(3000); // allow Dataverse write

  // Verify the new record appears in the gallery
  const newItem = canvasFrame
    .locator('[data-control-part="gallery-item"]')
    .filter({ hasText: accountName });

  await expect(newItem).toBeVisible({ timeout: 30000 });
});

Discover generated control names

Gen UX control names change when you regenerate the app. To find current control names:

  1. Open the app in play mode in a browser.
  2. Open browser DevTools (F12).
  3. Use the Inspector to hover over a control.
  4. Look for data-control-name in the element's attributes.

Tip

Prefer aria-label and role-based selectors over data-control-name for Gen UX apps. ARIA labels come from your Dataverse column display names and don't change when you regenerate the app.

Authentication

Gen UX apps use the Power Apps domain. Use the standard storage state:

import { getStorageStatePath } from 'power-platform-playwright-toolkit';

test.use({
  storageState: getStorageStatePath(process.env.MS_AUTH_EMAIL!),
});

Next steps

See also