umbraco-e2e-testing について
Playwright と Umbraco acceptance test helpers を使い、Umbraco バックオフィス拡張を E2E テストするスキルです。Umbraco の基本操作にはヘルパーを使い、拡張独自の UI には通常の Playwright 操作を使う方法を説明します。
主な機能
- 01起動中の Umbraco インスタンスを対象にテスト
- 02基本 UI と API 操作にヘルパーを使用
- 03拡張独自の要素を通常の Playwright で操作
- 04認証と再利用可能なフィクスチャを設定
- 05永続データをリセットしてテスト状態を統一
活用シーン
- 01バックオフィスの一連のユーザーワークフローを検証
- 02データ永続化と API 応答をテスト
- 03認証と認可を確認
- 04リリース前に受け入れテストを実行
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ umbraco/ umbraco-cms-backoffice-skills --skill umbraco-e2e-testingインストール後、Claude Code で/umbraco-e2e-testingを入力して読み込まれたか確認します。
同梱ファイル
SKILL.md と一緒にインストールされる参考資料やスクリプト。
examples/document-type-crud/.gitignorepackage-lock.jsonpackage.jsonplaywright.config.tsREADME.mdtsconfig.jsonumbraco.config.tsexamples/document-type-crud/tests/auth.setup.tsdocument-type.spec.ts詳細
Umbraco E2E テスト
Playwright と @umbraco-cms/acceptance-test-helpers を使用した Umbraco バックオフィス拡張機能のエンドツーエンドテストです。このアプローチでは、実際に稼働している Umbraco インスタンスに対してテストを実行し、完全なユーザーワークフローを検証します。
重要: コア Umbraco にはテストヘルパーを使用する
コア Umbraco の操作には @umbraco-cms/acceptance-test-helpers を使用してください:
| パッケージ | 目的 | 必要な理由 |
|---|---|---|
@umbraco-cms/acceptance-test-helpers | UI および API ヘルパー | 認証、ナビゲーション、コアエンティティの CRUD を処理します |
@umbraco/json-models-builders | テストデータビルダー | 正しい構造を持つ有効な Umbraco エンティティを作成します |
コア Umbraco にテストヘルパーを使用する理由
- Umbraco は
data-testidではなくdata-markを使用します - テストヘルパーがこれを処理します - 認証トークンの管理は複雑です - テストヘルパーが
STORAGE_STAGE_PATHを管理します - API のセットアップ/ティアダウンには特定のペイロード形式が必要です - ビルダーが正確性を保証します
- セレクターはバージョン間で変更されます - テストヘルパーがこれらを抽象化します
// WRONG - Raw Playwright for core Umbraco (brittle)
await page.goto('/umbraco');
await page.fill('[name="email"]', '[email protected]');
// CORRECT - Testhelpers for core Umbraco
import { test } from '@umbraco-cms/acceptance-test-helpers';
test('my test', async ({ umbracoApi, umbracoUi }) => {
await umbracoUi.goToBackOffice();
await umbracoUi.login.enterEmail('[email protected]');
});
生の Playwright をいつ使用するか
カスタム拡張機能には umbracoUi.page (生の Playwright) を使用します。テストヘルパーはあなたのカスタム要素を認識しないためです:
test('my custom extension', async ({ umbracoUi }) => {
// Testhelpers for core navigation
await umbracoUi.goToBackOffice();
await umbracoUi.content.goToSection(ConstantHelper.sections.settings);
// Raw Playwright for YOUR custom elements
await umbracoUi.page.getByRole('link', { name: 'My Custom Item' }).click();
await expect(umbracoUi.page.locator('my-custom-workspace')).toBeVisible();
});
| テストヘルパーを使用する対象 | umbracoUi.page を使用する対象 |
|---|---|
| ログイン/ログアウト | カスタムツリーアイテム |
| 任意のセクションへの移動 (カスタムを含む) | カスタムワークスペース要素 |
| API 経由でのドキュメントの作成/編集 | カスタムエンティティアクション |
| 組み込みの UI 操作 | カスタム UI コンポーネント |
使用するタイミング
- 完全なユーザーワークフローのテスト
- データ永続性のテスト
- 認証/認可のテスト
- リリース前の受け入れテスト
- 実際の API レスポンスを使用した統合テスト
関連スキル
- umbraco-testing - テストの概要に関するマスタースキル
- umbraco-playwright-testhelpers - テストヘルパーパッケージの完全なリファレンス
- umbraco-test-builders - テストデータ用の JsonModels.Builders
- umbraco-mocked-backoffice - 実際のバックエンドなしでのテスト (より高速)
ドキュメント
- Playwright: https://playwright.dev/docs/intro
- リファレンステスト:
Umbraco-CMS/tests/Umbraco.Tests.AcceptanceTest
セットアップ
依存関係
package.json に追加します:
{
"devDependencies": {
"@playwright/test": "^1.56",
"@umbraco-cms/acceptance-test-helpers": "^18.0.0",
"@umbraco/json-models-builders": "^18.0.0",
"dotenv": "^16.3.1"
},
"scripts": {
"test:e2e": "playwright test",
"test:e2e:ui": "playwright test --ui",
"test:e2e:debug": "playwright test --debug"
}
}
次に実行します:
npm install
npx playwright install chromium
バージョン互換性: テストヘルパーを Umbraco のバージョンに合わせてください:
| Umbraco | テストヘルパー |
|---|---|
| 18.x | ^18.0.0 |
| 17.x | ^17.0.15 |
| 14.x | ^14.x |
設定
playwright.config.ts を作成します:
import { defineConfig, devices } from '@playwright/test';
import { fileURLToPath } from 'url';
import { dirname, join } from 'path';
const __filename = fileURLToPath(import.meta.url);
const __dirname = dirname(__filename);
export const STORAGE_STATE = join(__dirname, 'tests/e2e/.auth/user.json');
// CRITICAL: Testhelpers read auth tokens from this file
process.env.STORAGE_STAGE_PATH = STORAGE_STATE;
export default defineConfig({
testDir: './tests/e2e',
timeout: 30 * 1000,
expect: { timeout: 5000 },
fullyParallel: false,
forbidOnly: !!process.env.CI,
retries: process.env.CI ? 2 : 0,
workers: 1,
reporter: process.env.CI ? 'line' : 'html',
use: {
baseURL: process.env.UMBRACO_URL || 'https://localhost:44325',
trace: 'retain-on-failure',
ignoreHTTPSErrors: true,
// CRITICAL: Umbraco uses 'data-mark' not 'data-testid'
testIdAttribute: 'data-mark',
},
projects: [
{
name: 'setup',
testMatch: '**/*.setup.ts',
},
{
name: 'e2e',
testMatch: '**/*.spec.ts',
dependencies: ['setup'],
use: {
...devices['Desktop Chrome'],
ignoreHTTPSErrors: true,
storageState: STORAGE_STATE,
},
},
],
});
重要な設定
| 設定 | 値 | 必要な理由 |
|---|---|---|
testIdAttribute | 'data-mark' | Umbraco は data-testid ではなく data-mark を使用します |
STORAGE_STAGE_PATH | user.json へのパス | テストヘルパーはこのファイルから認証トークンを読み取ります |
ignoreHTTPSErrors | true | 自己署名証明書を使用するローカル開発用 |
testIdAttribute: 'data-mark' がないと、すべての getByTestId() 呼び出しが失敗します。
認証のセットアップ
tests/e2e/auth.setup.ts を作成します:
import { test as setup } from '@playwright/test';
import { STORAGE_STATE } from '../../playwright.config';
import { ConstantHelper, UiHelpers } from '@umbraco-cms/acceptance-test-helpers';
setup('authenticate', async ({ page }) => {
const umbracoUi = new UiHelpers(page);
await umbracoUi.goToBackOffice();
await umbracoUi.login.enterEmail(process.env.UMBRACO_USER_LOGIN!);
await umbracoUi.login.enterPassword(process.env.UMBRACO_USER_PASSWORD!);
await umbracoUi.login.clickLoginButton();
await umbracoUi.login.goToSection(ConstantHelper.sections.settings);
await page.context().storageState({ path: STORAGE_STATE });
});
環境変数
.env を作成します (.gitignore に追加):
UMBRACO_URL=https://localhost:44325
[email protected]
UMBRACO_USER_PASSWORD=yourpassword
UMBRACO_DATA_PATH=/path/to/Umbraco.Web.UI/App_Data # Optional: for data reset
| 変数 | 必須 | 目的 |
|---|---|---|
UMBRACO_URL | はい | バックオフィスの URL |
UMBRACO_USER_LOGIN | はい | 管理者メールアドレス |
UMBRACO_USER_PASSWORD | はい | 管理者パスワード |
UMBRACO_DATA_PATH | いいえ | テストデータリセット用の App_Data パス (「永続データを使用したテスト」を参照) |
ディレクトリ構造
my-extension/
├── src/
│ └── ...
├── tests/
│ └── e2e/
│ ├── .auth/
│ │ └── user.json # Auth state (gitignored)
│ ├── auth.setup.ts # Authentication
│ └── my-extension.spec.ts
├── playwright.config.ts
├── .env # Gitignored
├── .env.example
└── package.json
パターン
テストフィクスチャ
import { test } from '@umbraco-cms/acceptance-test-helpers';
test('my test', async ({ umbracoApi, umbracoUi }) => {
// umbracoApi - API helpers for setup/teardown
// umbracoUi - UI helpers for backoffice interaction
});
AAA パターン (Arrange-Act-Assert)
test('can create content', async ({ umbracoApi, umbracoUi }) => {
// Arrange - Setup via API
await umbracoApi.documentType.createDefaultDocumentType('TestDocType');
// Act - Perform user actions via UI
await umbracoUi.goToBackOffice();
await umbracoUi.content.goToSection(ConstantHelper.sections.content);
await umbracoUi.content.clickActionsMenuAtRoot();
// Assert - Verify results
expect(await umbracoApi.document.doesNameExist('TestContent')).toBeTruthy();
});
冪等なクリーンアップ
test.afterEach(async ({ umbracoApi }) => {
await umbracoApi.document.ensureNameNotExists(contentName);
await umbracoApi.documentType.ensureNameNotExists(documentTypeName);
});
API ヘルパー (umbracoApi)
ドキュメントタイプ:
await umbracoApi.documentType.createDefaultDocumentType('TestDocType');
await umbracoApi.documentType.createDocumentTypeWithPropertyEditor(
'TestDocType', 'Textstring', dataTypeData.id
);
await umbracoApi.documentType.ensureNameNotExists('TestDocType');
ドキュメント:
await umbracoApi.document.createDefaultDocument('TestContent', docTypeId);
await umbracoApi.document.createDocumentWithTextContent(
'TestContent', docTypeId, 'value', 'Textstring'
);
await umbracoApi.document.publish(contentId);
await umbracoApi.document.ensureNameNotExists('TestContent');
データタイプ:
const dataType = await umbracoApi.dataType.getByName('Textstring');
await umbracoApi.dataType.create('MyType', 'Umbraco.TextBox', 'Umb.PropertyEditorUi.TextBox', []);
複雑なデータにビルダーを使用する
複雑なテストデータには @umbraco/json-models-builders を使用します:
import { DocumentTypeBuilder, DocumentBuilder } from '@umbraco/json-models-builders';
test('create complex document type', async ({ umbracoApi }) => {
// Build a document type with multiple properties
const docType = new DocumentTypeBuilder()
.withName('Article')
.withAlias('article')
.addGroup()
.withName('Content')
.addTextBoxProperty()
.withAlias('title')
.withLabel('Title')
.done()
.addRichTextProperty()
.withAlias('body')
.withLabel('Body')
.done()
.done()
.build();
await umbracoApi.documentType.create(docType);
});
ビルダーを使用する理由
- Fluent API により複雑な構造が読みやすくなります
- Umbraco API に対して有効なペイロード構造を保証します
- 必須フィールドとデフォルト値を処理します
- TypeScript で型安全です
UI ヘルパー (umbracoUi)
ナビゲーション:
await umbracoUi.goToBackOffice();
await umbracoUi.content.goToSection(ConstantHelper.sections.content);
await umbracoUi.content.goToContentWithName('My Page');
サイドバーでカスタムツリーをテストする
カスタムツリー拡張機能(例: 設定内)をテストする際は、このパターンを使用して非同期読み込みとスクロールを処理します:
test('should click custom tree item', async ({ umbracoUi }) => {
await umbracoUi.goToBackOffice();
await umbracoUi.content.goToSection(ConstantHelper.sections.settings);
// 1. Wait for your tree heading (custom trees often at bottom of sidebar)
await umbracoUi.page.getByRole('heading', { name: 'My Tree' }).waitFor({ timeout: 15000 });
// 2. Scroll into view (important - sidebar may be long)
await umbracoUi.page.getByRole('heading', { name: 'My Tree' }).scrollIntoViewIfNeeded();
// 3. Wait for tree items to load (async from API)
const item1Link = umbracoUi.page.getByRole('link', { name: 'Item 1' });
await item1Link.waitFor({ timeout: 15000 });
// 4. Click the item
await item1Link.click();
// Assert workspace loads
await expect(umbracoUi.page.locator('my-tree-workspace-editor')).toBeVisible({ timeout: 15000 });
});
なぜこのパターンなのか?
- カスタムツリーは多くの場合、設定サイドバーの下部にあります
- ツリー項目は API から非同期で読み込まれます
getByRole('link', { name: '...' })を使用する方が、汎用的なumb-tree-itemセレクターよりも信頼性が高いです- 組み込みのツリー(ドキュメントタイプなど)も
umb-tree-itemを使用するため、セレクターが競合します
コンテンツアクション:
await umbracoUi.content.clickActionsMenuAtRoot();
await umbracoUi.content.clickCreateActionMenuOption();
await umbracoUi.content.chooseDocumentType('TestDocType');
await umbracoUi.content.enterContentName('My Page');
await umbracoUi.content.enterTextstring('My text value');
await umbracoUi.content.clickSaveButton();
定数:
import { ConstantHelper } from '@umbraco-cms/acceptance-test-helpers';
ConstantHelper.sections.content
ConstantHelper.sections.settings
ConstantHelper.buttons.save
ConstantHelper.buttons.saveAndPublish
例
完全なテスト
import { expect } from '@playwright/test';
import { ConstantHelper, NotificationConstantHelper, test } from '@umbraco-cms/acceptance-test-helpers';
const contentName = 'TestContent';
const documentTypeName = 'TestDocType';
const dataTypeName = 'Textstring';
const contentText = 'Test content text';
test.afterEach(async ({ umbracoApi }) => {
await umbracoApi.document.ensureNameNotExists(contentName);
await umbracoApi.documentType.ensureNameNotExists(documentTypeName);
});
test('can create content', { tag: '@smoke' }, async ({ umbracoApi, umbracoUi }) => {
// Arrange
const dataTypeData = await umbracoApi.dataType.getByName(dataTypeName);
await umbracoApi.documentType.createDocumentTypeWithPropertyEditor(
documentTypeName, dataTypeName, dataTypeData.id
);
// Act
await umbracoUi.goToBackOffice();
await umbracoUi.content.goToSection(ConstantHelper.sections.content);
await umbracoUi.content.clickActionsMenuAtRoot();
await umbracoUi.content.clickCreateActionMenuOption();
await umbracoUi.content.chooseDocumentType(documentTypeName);
await umbracoUi.content.enterContentName(contentName);
await umbracoUi.content.enterTextstring(contentText);
await umbracoUi.content.clickSaveButton();
// Assert
await umbracoUi.content.waitForContentToBeCreated();
expect(await umbracoApi.document.doesNameExist(contentName)).toBeTruthy();
const contentData = await umbracoApi.document.getByName(contentName);
expect(contentData.values[0].value).toBe(contentText);
});
test('can publish content', async ({ umbracoApi, umbracoUi }) => {
// Arrange
const dataTypeData = await umbracoApi.dataType.getByName(dataTypeName);
const docTypeId = await umbracoApi.documentType.createDocumentTypeWithPropertyEditor(
documentTypeName, dataTypeName, dataTypeData.id
);
await umbracoApi.document.createDocumentWithTextContent(
contentName, docTypeId, contentText, dataTypeName
);
// Act
await umbracoUi.goToBackOffice();
await umbracoUi.content.goToSection(ConstantHelper.sections.content);
await umbracoUi.content.clickActionsMenuForContent(contentName);
await umbracoUi.content.clickPublishActionMenuOption();
await umbracoUi.content.clickConfirmToPublishButton();
// Assert
await umbracoUi.content.doesSuccessNotificationHaveText(
NotificationConstantHelper.success.published
);
const contentData = await umbracoApi.document.getByName(contentName);
expect(contentData.variants[0].state).toBe('Published');
});
動作する例: tree-example
tree-example はカスタムツリー拡張機能の E2E テストを示しています:
場所: umbraco-backoffice/examples/tree-example/Client/
# Run E2E tests (requires running Umbraco)
URL=https://localhost:44325 \
[email protected] \
UMBRACO_USER_PASSWORD=yourpassword \
npm run test:e2e # 7 tests
主要なファイル:
tests/playwright.e2e.config.ts- 認証設定を含む E2E 設定tests/auth.setup.ts- testhelpers を使用した認証tests/tree-e2e.spec.ts- 設定サイドバー内のカスタムツリーのテスト
動作する例: notes-wiki(データリセットを伴うフルスタック)
notes-wiki は永続データとCRUD 操作を伴う E2E テストを示しています:
場所: umbraco-backoffice/examples/notes-wiki/Client/
# Run E2E tests (with data reset)
URL=https://localhost:44325 \
[email protected] \
UMBRACO_USER_PASSWORD=yourpassword \
UMBRACO_DATA_PATH=/path/to/App_Data \
npm run test:e2e # 16 tests
主要なファイル:
tests/playwright.e2e.config.ts- データリセット用のglobalSetupを含む設定tests/global-setup.ts- テスト前にデータをシード状態にリセットしますtests/test-seed-data.json- 既知のテストデータ(ノート、フォルダ)tests/notes-wiki-e2e.spec.ts- CRUD とナビゲーションのテスト
これが示すもの:
goToSection('notes')を使用したカスタムセクションのテスト- 各テスト実行前にファイルベースのデータをリセットする方法
- ツリーナビゲーション、フォルダ、ワークスペースのテスト
- 「View actions」ボタン経由のエンティティアクション(右クリックより信頼性が高い)
- ダッシュボードとワークスペースビューのテスト
永続データを伴う拡張機能のテスト
拡張機能がデータ(JSON ファイル、データベースなど)を永続化する場合、テストには予測可能な初期状態が必要です。
グローバルセットアップパターン
テスト前にデータをリセットするために globalSetup を追加します:
playwright.e2e.config.ts:
export default defineConfig({
// ... other config
globalSetup: './global-setup.ts',
});
global-setup.ts:
import { FullConfig } from '@playwright/test';
import * as fs from 'fs';
import * as path from 'path';
async function globalSetup(config: FullConfig) {
const dataPath = process.env.UMBRACO_DATA_PATH;
if (!dataPath) {
console.warn('⚠️ UMBRACO_DATA_PATH not set. Skipping data reset.');
return;
}
const targetFile = path.join(dataPath, 'MyExtension/data.json');
const seedFile = path.join(__dirname, 'test-seed-data.json');
// Ensure directory exists
fs.mkdirSync(path.dirname(targetFile), { recursive: true });
// Copy seed data to target
fs.copyFileSync(seedFile, targetFile);
console.log('🌱 Reset data to seed state');
}
export default globalSetup;
test-seed-data.json:
{
"items": [
{ "id": "test-1", "name": "Test Item 1" },
{ "id": "test-2", "name": "Test Item 2" }
]
}
環境変数
Umbraco の App_Data フォルダを特定するために UMBRACO_DATA_PATH を追加します:
UMBRACO_DATA_PATH=/path/to/Umbraco.Web.UI/App_Data npm run test:e2e
カスタムセクションのテスト
カスタムセクションは testhelpers の goToSection() メソッドで動作します - セクションのパス名を渡します:
// Section pathname - matches what you defined in section/constants.ts
const MY_SECTION = 'my-section';
// Helper to navigate to custom section using testhelpers
async function goToMySection(umbracoUi: any) {
await umbracoUi.goToBackOffice();
await umbracoUi.content.goToSection(MY_SECTION);
await umbracoUi.page.waitForTimeout(500);
}
test('should navigate to custom section', async ({ umbracoUi }) => {
await goToMySection(umbracoUi);
// Assert - your dashboard or tree should be visible
await expect(umbracoUi.page.getByText('Welcome')).toBeVisible({ timeout: 15000 });
});
// To verify the section exists in the section bar:
test('should display my section', async ({ umbracoUi }) => {
await umbracoUi.goToBackOffice();
await expect(umbracoUi.page.getByRole('tab', { name: 'My Section' })).toBeVisible({ timeout: 15000 });
});
コンテキストメニュー(エンティティアクション)のテスト
ツリー項目のエンティティアクションをテストします。testhelpers はカスタムエンティティアクションをカバーしていないため、umbracoUi.page を使用します。
重要: Umbraco のエンティティアクションは、直接 menuitem ロールとしてではなく、ドロップダウンメニュー内のボタンとしてレンダリングされます。最も信頼性の高いアプローチは、右クリックではなく「View actions」ボタンを使用することです:
test('should show delete action via actions button', async ({ umbracoUi }) => {
await goToMySection(umbracoUi);
// Wait for tree item
const itemLink = umbracoUi.page.getByRole('link', { name: 'My Item' });
await itemLink.waitFor({ timeout: 15000 });
// Hover to reveal action buttons
await itemLink.hover();
// Click the "View actions" button to open dropdown
const actionsButton = umbracoUi.page.getByRole('button', { name: "View actions for 'My Item'" });
await actionsButton.click();
// Wait for dropdown and check for actions (actions are BUTTONS, not menuitems!)
await umbracoUi.page.waitForTimeout(500);
const deleteButton = umbracoUi.page.getByRole('button', { name: 'Delete' });
const renameButton = umbracoUi.page.getByRole('button', { name: 'Rename' });
// Assert - at least one action should be visible
await expect(deleteButton.or(renameButton)).toBeVisible({ timeout: 5000 });
});
test('should delete item via actions menu', async ({ umbracoUi }) => {
await goToMySection(umbracoUi);
const itemLink = umbracoUi.page.getByRole('link', { name: 'Item to Delete' });
await itemLink.waitFor({ timeout: 15000 });
// Hover and open actions menu
await itemLink.hover();
await umbracoUi.page.getByRole('button', { name: "View actions for 'Item to Delete'" }).click();
// Click delete button
await umbracoUi.page.getByRole('button', { name: 'Delete' }).click();
// Confirm deletion (if modal appears)
const confirmButton = umbracoUi.page.getByRole('button', { name: /Confirm|Delete/i });
if (await confirmButton.isVisible({ timeout: 2000 }).catch(() => false)) {
await confirmButton.click();
}
// Assert - item should be gone
await expect(itemLink).not.toBeVisible({ timeout: 15000 });
});
代替手段: 右クリックコンテキストメニュー
右クリックも機能しますが、アクションボタンのアプローチの方が信頼性が高いです:
// Right-click approach (less reliable than actions button)
await itemLink.click({ button: 'right' });
await umbracoUi.page.waitForTimeout(500);
await umbracoUi.page.getByRole('button', { name: 'Delete' }).click();
CRUD テストパターン
カスタム拡張機能には、UI 操作に umbracoUi.page を使用します。Umbraco コアのコンテンツには、セットアップ/ティアダウンに umbracoApi ヘルパーを優先します。
アクションメニュー経由での作成(カスタム拡張機能)
test('should create new item', async ({ umbracoUi }) => {
await goToMySection(umbracoUi);
// Hover over parent folder and use Create button
const folderLink = umbracoUi.page.getByRole('link', { name: 'Parent Folder' });
await folderLink.hover();
// Scope to specific menu to avoid ambiguity with multiple items
const folderMenu = umbracoUi.page.getByRole('menu').filter({ hasText: 'Parent Folder' });
const createButton = folderMenu.getByRole('button', { name: 'Create Note' });
await createButton.click();
// Assert - workspace should open with "New" indicator
await expect(umbracoUi.page.locator('my-workspace-editor')).toBeVisible({ timeout: 15000 });
});
特定のツリー項目へのスコープ設定
類似した要素を持つ複数のツリー項目が存在する場合、曖昧さを避けるためにセレクターをスコープ設定します:
// WRONG - ambiguous when multiple folders have "Create" buttons
const createButton = page.getByRole('button', { name: 'Create' });
// CORRECT - scoped to specific folder's menu
const folderMenu = page.getByRole('menu').filter({ hasText: 'My Folder' });
const createButton = folderMenu.getByRole('button', { name: 'Create' });
更新と保存
test('should update item', async ({ umbracoUi }) => {
await goToMySection(umbracoUi);
// Navigate to item
await umbracoUi.page.getByRole('link', { name: 'Test Item' }).click();
await umbracoUi.page.locator('my-workspace-editor').waitFor({ timeout: 15000 });
// Update field
const titleInput = umbracoUi.page.locator('uui-input#title');
await titleInput.clear();
await titleInput.fill('Updated Title');
// Save
await umbracoUi.page.getByRole('button', { name: /Save/i }).click();
// Wait for save to complete
await umbracoUi.page.waitForTimeout(2000);
// Assert - header should reflect change
await expect(umbracoUi.page.getByText('Updated Title')).toBeVisible();
});
テストの実行
# Run all E2E tests
npm run test:e2e
# Run with UI mode (visual debugging)
npm run test:e2e:ui
# Run specific test file
npx playwright test tests/e2e/my-extension.spec.ts
# Run with specific tag
npx playwright test --grep "@smoke"
# Run in debug mode
npx playwright test --debug
トラブルシューティング
getByTestId() が要素を見つけられない
playwright.config.ts で testIdAttribute: 'data-mark' が設定されていることを確認してください。
認証が失敗する
.envの認証情報が正しいことを確認してください- Umbraco インスタンスが実行中であることを確認してください
STORAGE_STAGE_PATHが設定されていることを確認してください
テストがタイムアウトする
- 設定のタイムアウトを増やしてください
- Umbraco が応答していることを確認してください
- ブラウザコンソールで JS エラーを確認してください
CI でテストが失敗する
- Umbraco インスタンスにアクセスできることを確認してください
- CI で環境変数を設定してください
npx playwright install chromiumを使用してください
代替案: MSW モード (バックエンド不要)
実際の Umbraco バックエンドを使用せずに、より高速にテストするには、モック化されたバックオフィスのアプローチを使用します。
呼び出し: skill: umbraco-mocked-backoffice
| 項目 | 実際のバックエンド (このスキル) | MSW モード |
|---|---|---|
| セットアップ | 実行中の Umbraco インスタンス | Umbraco-CMS をクローン、npm install |
| 認証 | 必須 | 不要 |
| 速度 | 遅い | 速い |
| 用途 | 統合/受け入れ | UI/コンポーネントテスト |