mcpField
U
umbraco-e2e-testing

umbraco-e2e-testing

github.com/umbracoテスト・品質27372 回インストール6 週間前に更新
リポジトリ

umbraco-e2e-testing について

Playwright と Umbraco acceptance test helpers を使い、Umbraco バックオフィス拡張を E2E テストするスキルです。Umbraco の基本操作にはヘルパーを使い、拡張独自の UI には通常の Playwright 操作を使う方法を説明します。

主な機能

  1. 01起動中の Umbraco インスタンスを対象にテスト
  2. 02基本 UI と API 操作にヘルパーを使用
  3. 03拡張独自の要素を通常の Playwright で操作
  4. 04認証と再利用可能なフィクスチャを設定
  5. 05永続データをリセットしてテスト状態を統一

活用シーン

  1. 01バックオフィスの一連のユーザーワークフローを検証
  2. 02データ永続化と API 応答をテスト
  3. 03認証と認可を確認
  4. 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/
.gitignore
package-lock.json
package.json
playwright.config.ts
README.md
tsconfig.json
umbraco.config.ts
examples/document-type-crud/tests/
auth.setup.ts
document-type.spec.ts

詳細

Umbraco E2E テスト

Playwright と @umbraco-cms/acceptance-test-helpers を使用した Umbraco バックオフィス拡張機能のエンドツーエンドテストです。このアプローチでは、実際に稼働している Umbraco インスタンスに対してテストを実行し、完全なユーザーワークフローを検証します。

重要: コア Umbraco にはテストヘルパーを使用する

コア Umbraco の操作には @umbraco-cms/acceptance-test-helpers を使用してください:

パッケージ目的必要な理由
@umbraco-cms/acceptance-test-helpersUI および 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 - 実際のバックエンドなしでのテスト (より高速)

ドキュメント


セットアップ

依存関係

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_PATHuser.json へのパステストヘルパーはこのファイルから認証トークンを読み取ります
ignoreHTTPSErrorstrue自己署名証明書を使用するローカル開発用

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/コンポーネントテスト

コメント