キャンバス アプリは、Power Apps プレーヤー内の iframe 内で実行されます。 このガイドでは、キャンバス アプリを起動し、セレクターのスコープを適切なフレームに設定し、 data-control-name 属性を使用してコントロールを操作する方法について説明します。
キャンバス アプリのテストのしくみ
キャンバス アプリが再生モードで読み込まれると、ランタイムは iframe内でアプリをホストします。
iframe[name="fullscreen-app-host"]
アプリ内のすべてのコントロールには、Power Apps Studio で設定したコントロール名と一致する data-control-name 属性があります。 ギャラリーアイテムには data-control-part="gallery-item"があります。
コントロールを操作する前に、すべてのロケーターのスコープをこのフレームに設定します。
const canvasFrame = page.frameLocator('iframe[name="fullscreen-app-host"]');
キャンバス アプリを起動する
AppProviderを使用してアプリを起動し、CanvasAppPage オブジェクトを取得します。
import { test, expect } from '@playwright/test';
import {
AppProvider,
AppType,
AppLaunchMode,
buildCanvasAppUrlFromEnv,
} from 'power-platform-playwright-toolkit';
const CANVAS_APP_URL = buildCanvasAppUrlFromEnv();
test.beforeEach(async ({ page, context }) => {
const app = new AppProvider(page, context);
await app.launch({
app: 'Northwind Orders',
type: AppType.Canvas,
mode: AppLaunchMode.Play,
skipMakerPortal: true, // bypasses Power Apps navigation
directUrl: CANVAS_APP_URL,
});
});
ヒント
skipMakerPortal: trueを設定し、directUrlを指定すると、Power Appsナビゲーションをバイパスすることで、テストごとに 10 秒から 20 秒を節約できます。
アプリが読み込まれるのを待つ
起動後、次の操作を行う前に、既知のコントロールが表示されるのを待ちます。
const canvasFrame = page.frameLocator('iframe[name="fullscreen-app-host"]');
// Wait for gallery to confirm the app is loaded and data is present
await canvasFrame
.locator('[data-control-name="Gallery1"] [data-control-part="gallery-item"]')
.first()
.waitFor({ state: 'visible', timeout: 60000 });
注
Dataverse によってサポートされるキャンバス アプリは、ギャラリーにデータを読み込むのに 30 ~ 60 秒かかる場合があります。 ギャラリー セレクターには 60 秒のタイムアウトを使用します。
コントロールを操作する
次の例では、フレーム スコープ ロケーターを使用して、一般的なキャンバス アプリ コントロールを操作する方法を示します。
ボタンをクリックする
data-control-name属性でボタンを探し、ボタンが表示されるまで待ってからクリックします。
const addButton = canvasFrame.locator('[data-control-name="icon3"]');
await addButton.waitFor({ state: 'visible', timeout: 10000 });
await addButton.click();
テキスト入力欄に入力する
fill() メソッドを使用して、テキスト入力の値を設定し、そのaria-labelをターゲットにします。
const orderNumberInput = canvasFrame.locator('input[aria-label="Order Number"]');
await orderNumberInput.fill('ORD-12345');
ギャラリー項目を選択する
表示されたテキスト コンテンツでギャラリー 項目をフィルター処理し、特定のレコードを検索してクリックします。
const galleryItem = canvasFrame
.locator('[data-control-part="gallery-item"]')
.filter({ has: canvasFrame.locator('[data-control-name="Title1"]').getByText('Order 001') });
await galleryItem.waitFor({ state: 'visible' });
await galleryItem.click();
ギャラリーの項目を数える
count() メソッドを使用して、ギャラリーに予想される項目数が含まれていることを確認します。
const galleryItems = canvasFrame.locator('[data-control-name="Gallery1"] [data-control-part="gallery-item"]');
const count = await galleryItems.count();
expect(count).toBeGreaterThan(0);
キャンバス アプリのページ オブジェクトを作成する
保守性を確保するために、Page オブジェクト クラスにセレクターとアクションをカプセル化します。
// pages/my-app/MyCanvasAppPage.ts
import { Page, FrameLocator } from '@playwright/test';
export class MyCanvasAppPage {
private readonly frame: FrameLocator;
constructor(private readonly page: Page) {
this.frame = page.frameLocator('iframe[name="fullscreen-app-host"]');
}
get addButton() {
return this.frame.locator('[data-control-name="AddButton"]');
}
get gallery() {
return this.frame.locator('[data-control-name="Gallery1"]');
}
async waitForLoad(): Promise<void> {
await this.gallery
.locator('[data-control-part="gallery-item"]')
.first()
.waitFor({ state: 'visible', timeout: 60000 });
}
async clickAdd(): Promise<void> {
await this.addButton.click();
}
async getItemCount(): Promise<number> {
return this.gallery.locator('[data-control-part="gallery-item"]').count();
}
}
キャンバス アプリの完全な CRUD テストの例
この例では、アプリの起動、ギャラリーの検証、フォームの操作を完全なテスト スイートに組み合わせます。
import { test, expect, FrameLocator } from '@playwright/test';
import { AppProvider, AppType, AppLaunchMode, buildCanvasAppUrlFromEnv } from 'power-platform-playwright-toolkit';
const CANVAS_APP_URL = buildCanvasAppUrlFromEnv();
test.describe('Canvas App - Orders', () => {
let canvasFrame: FrameLocator;
test.beforeEach(async ({ page, context }) => {
const app = new AppProvider(page, context);
await app.launch({
app: 'Orders App',
type: AppType.Canvas,
mode: AppLaunchMode.Play,
skipMakerPortal: true,
directUrl: CANVAS_APP_URL,
});
canvasFrame = page.frameLocator('iframe[name="fullscreen-app-host"]');
await canvasFrame
.locator('[data-control-part="gallery-item"]')
.first()
.waitFor({ state: 'visible', timeout: 60000 });
});
test('should display orders in gallery', async () => {
const count = await canvasFrame
.locator('[data-control-part="gallery-item"]')
.count();
expect(count).toBeGreaterThan(0);
});
test('should click Add and show form', async ({ page }) => {
await canvasFrame.locator('[data-control-name="icon3"]').click();
await page.waitForTimeout(2000);
const input = canvasFrame.locator('input[type="text"]').first();
await expect(input).toBeVisible();
});
});
キャンバス アプリでコントロール名を検出する
アプリで data-control-name 値を検索するには:
- ブラウザーで再生モードでアプリを開きます。
- ブラウザー開発者ツールを開きます (F12)。
-
インスペクターを使用してコントロールにカーソルを合わせ、
data-control-name属性を探します。
または、Playwright MCP サーバーを使用して、AI アシスタントに DOM の検査とセレクターの生成を依頼します。 AI 支援テストを参照してください。