mcpField
U
umbraco-mocked-backoffice

umbraco-mocked-backoffice

github.com/umbraco测试与质量27374 次安装6 周前更新
代码仓库

关于 umbraco-mocked-backoffice

用于在 API 调用被模拟的 Umbraco 后台界面中测试扩展的技能,无需 .NET 后端。它介绍 MSW 和模拟仓库方案及 Playwright 测试配置;外部扩展支持仍待 Umbraco 更新。

主要功能

  1. 01无需 .NET 后端运行后台界面
  2. 02使用 MSW 处理器模拟 API
  3. 03用模拟仓库提供可预测数据
  4. 04配置扩展的 Playwright 测试
  5. 05介绍后台 API 的模拟数据

适用场景

  1. 01开发期间目视测试扩展
  2. 02通过模拟请求测试加载和错误状态
  3. 03无需后端配置运行扩展界面测试
  4. 04无需基础设施展示扩展

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/umbraco/umbraco-cms-backoffice-skills --skill umbraco-mocked-backoffice

安装后在 Claude Code 中输入/umbraco-mocked-backoffice确认已加载。

附带文件patterns/external-msw-handlers.mdpatterns/mock-repository-pattern.md

详细介绍

Umbraco 模拟后台

状态: 这项技能目前正在等待 Umbraco 更新,以便外部扩展能够使用模拟后台。本文档介绍的模式在 Umbraco-CMS 源代码仓库中运行时有效。

运行完整的 Umbraco 后台 UI,并模拟所有 API 调用——无需 .NET 后端。

适用场景

  • 在开发过程中对扩展进行视觉测试
  • 无需部署后端即可快速迭代
  • 在逼真的 UI 环境中测试扩展
  • 无需基础设施即可演示扩展
  • 无需配置后端即可进行 CI/CD 测试

相关技能

  • umbraco-example-generator - 为模拟后台设置扩展(从这里开始)
  • umbraco-testing - 测试概览的主技能
  • umbraco-unit-testing - 独立测试扩展逻辑
  • umbraco-e2e-testing - 针对真实的 Umbraco 实例进行测试

两种模拟方式

具有自定义 API 的扩展可以使用两种模拟方式:

方式使用场景最适合
MSW 处理程序网络级 API 模拟测试错误处理、加载状态、重试
模拟存储库应用级模拟使用可预测的数据测试 UI(推荐)

这两种方式都需要启用 MSW(VITE_UMBRACO_USE_MSW=on),以模拟 Umbraco 核心 API。


设置

创建扩展

使用 umbraco-example-generator 技能设置扩展:

调用:skill: umbraco-example-generator

包括:

  • 克隆 Umbraco-CMS 仓库
  • 扩展结构和 src/index.ts 要求
  • 使用 VITE_EXAMPLE_PATH 和 npm run dev 运行
添加测试依赖项
{
  "devDependencies": {
    "@playwright/test": "^1.56"
  },
  "scripts": {
    "test:mock-repo": "playwright test --config=tests/mock-repo/playwright.config.ts",
    "test:msw": "playwright test --config=tests/msw/playwright.config.ts"
  }
}
npm install
npx playwright install chromium
目录结构
my-extension/Client/
├── src/
│   ├── index.ts                # Entry point (loads manifests, registers MSW handlers)
│   ├── manifests.ts            # Production manifests
│   ├── feature/
│   │   ├── my-element.ts
│   │   └── types.ts
│   └── msw/                    # MSW handlers (loaded from index.ts)
│       └── handlers.ts
├── tests/
│   ├── mock-repo/              # Mock repository tests
│   │   ├── playwright.config.ts
│   │   ├── my-extension.spec.ts
│   │   └── mock/
│   │       ├── index.ts        # Mock manifests (replaces repository)
│   │       ├── mock-repository.ts
│   │       └── mock-data.ts
│   └── msw/                    # MSW tests
│       ├── playwright.config.ts
│       └── my-extension.spec.ts
├── package.json
└── tsconfig.json

入口点(src/index.ts)

入口点会根据环境有条件地加载 MSW 处理程序或模拟清单:

// Entry point for external extension loading
// Run from Umbraco.Web.UI.Client with:
//   VITE_EXAMPLE_PATH=/path/to/extension/Client VITE_UMBRACO_USE_MSW=on npm run dev
//   VITE_EXAMPLE_PATH=/path/to/extension/Client VITE_USE_MOCK_REPO=on VITE_UMBRACO_USE_MSW=on npm run dev

// Register MSW handlers when running in MSW mode (but not mock-repo mode)
if (import.meta.env.VITE_UMBRACO_USE_MSW === 'on' && import.meta.env.VITE_USE_MOCK_REPO !== 'on') {
  import('./msw/handlers.js').then(({ createHandlers }) => {
    const { addMockHandlers } = (window as any).MockServiceWorker;
    addMockHandlers(...createHandlers());
  });
}

// Export manifests - use mock repository if VITE_USE_MOCK_REPO is set
export const manifests = import.meta.env.VITE_USE_MOCK_REPO === 'on'
  ? (await import('../tests/mock-repo/mock/index.js')).manifests
  : (await import('./manifests.js')).manifests;

运行测试

独立支持外部示例(自动)

从 Umbraco 客户端之外的路径加载扩展,需要客户端支持绝对路径形式的 VITE_EXAMPLE_PATH(通过 Vite 的 /@fs/),并提供共享包解析器,以便 @umbraco-cms/backoffice、lit 和 @umbraco-ui/uui 都解析到客户端中的同一份副本。正式发布的 v18 仅支持相对路径(./examples/...)。

为保持独立性,本仓库的模拟配置通过 Playwright globalSetup/globalTeardown (plugins/umbraco-testing-skills/harness/mocked-backoffice/),在开发服务器启动前向 UMBRACO_CLIENT_PATH 指定的客户端注入此支持,并在之后还原 (基于文件备份;会检测客户端是否已具备该支持,如有则不作处理)。因此无需使用 CMS 分支——只需一个已构建的 v18 客户端(在 Umbraco.Web.UI.Client 中运行 npm ci)。

首次在刚打过补丁的客户端上运行时,可能会重新优化依赖并短暂重启开发服务器(首次导航可能不稳定);配置中设定了 retries,以应对这种情况。后续运行稳定。

环境变量
变量值用途
VITE_EXAMPLE_PATH/path/to/extension/Client扩展目录的路径
VITE_UMBRACO_USE_MSWon为 Umbraco 核心 API 启用 MSW
VITE_USE_MOCK_REPOon使用模拟存储库代替 MSW 处理程序
UMBRACO_CLIENT_PATH/path/to/Umbraco.Web.UI.ClientUmbraco 客户端的路径(用于 Playwright)
手动启动开发服务器
cd /path/to/Umbraco-CMS/src/Umbraco.Web.UI.Client

# MSW mode (uses your handlers for custom APIs)
VITE_EXAMPLE_PATH=/path/to/extension/Client VITE_UMBRACO_USE_MSW=on npm run dev

# Mock repository mode (uses mock repository for custom APIs)
VITE_EXAMPLE_PATH=/path/to/extension/Client VITE_USE_MOCK_REPO=on VITE_UMBRACO_USE_MSW=on npm run dev
运行测试
cd /path/to/extension/Client

# Set path to Umbraco client
export UMBRACO_CLIENT_PATH=/path/to/Umbraco-CMS/src/Umbraco.Web.UI.Client

# Run MSW tests
npm run test:msw

# Run mock repository tests
npm run test:mock-repo

Playwright 配置示例

创建 tests/msw/playwright.config.ts:

import { defineConfig, devices } from '@playwright/test';
import { fileURLToPath } from 'url';
import { dirname, resolve } from 'path';

const __filename = fileURLToPath(import.meta.url);
const __dirname = dirname(__filename);

const EXTENSION_PATH = resolve(__dirname, '../..');
const UMBRACO_CLIENT_PATH = process.env.UMBRACO_CLIENT_PATH;
if (!UMBRACO_CLIENT_PATH) {
  throw new Error('UMBRACO_CLIENT_PATH environment variable is required');
}

const DEV_SERVER_PORT = 5176;

export default defineConfig({
  testDir: '.',
  testMatch: ['*.spec.ts'],
  timeout: 60000,
  expect: { timeout: 15000 },
  fullyParallel: false,
  workers: 1,

  // Start dev server with extension and MSW enabled
  webServer: {
    command: `VITE_EXAMPLE_PATH=${EXTENSION_PATH} VITE_UMBRACO_USE_MSW=on npm run dev -- --port ${DEV_SERVER_PORT}`,
    cwd: UMBRACO_CLIENT_PATH,
    port: DEV_SERVER_PORT,
    reuseExistingServer: !process.env.CI,
    timeout: 120000,
  },

  use: {
    baseURL: `http://localhost:${DEV_SERVER_PORT}`,
    trace: 'on-first-retry',
    screenshot: 'only-on-failure',
  },
  projects: [
    {
      name: 'chromium',
      use: { ...devices['Desktop Chrome'] },
    },
  ],
});

对于模拟存储库测试,将命令改为包含 VITE_USE_MOCK_REPO=on:

command: `VITE_EXAMPLE_PATH=${EXTENSION_PATH} VITE_USE_MOCK_REPO=on VITE_UMBRACO_USE_MSW=on npm run dev -- --port ${DEV_SERVER_PORT}`,

测试模式

导航辅助函数
import { type Page } from '@playwright/test';

async function navigateToSettings(page: Page) {
  await page.goto('/section/settings');
  await page.waitForLoadState('domcontentloaded');
  await page.waitForSelector('umb-section-sidebar', { timeout: 30000 });
}
测试树项目
test('should display root tree items', async ({ page }) => {
  await navigateToSettings(page);

  await page.waitForSelector('umb-tree-item', { timeout: 15000 });
  const treeItems = page.locator('umb-tree-item');
  await expect(treeItems.first()).toBeVisible();
});

test('should expand tree item to show children', async ({ page }) => {
  await navigateToSettings(page);

  const expandableItem = page.locator('umb-tree-item').filter({ hasText: 'Group A' });
  const expandButton = expandableItem.locator('button[aria-label="toggle child items"]');
  await expandButton.click();

  const childItem = page.locator('umb-tree-item').filter({ hasText: 'Child 1' });
  await expect(childItem).toBeVisible({ timeout: 15000 });
});
MSW 模拟文档 URL
文档名称URL 路径
最简单的文档/section/content/workspace/document/edit/the-simplest-document-id
所有属性/section/content/workspace/document/edit/all-property-editors-document-id

故障排除

扩展未显示
  • 检查扩展是否从 src/index.ts 导出了 manifests 数组
  • 检查浏览器控制台中是否有错误
  • 确认 VITE_EXAMPLE_PATH 指向 Client 目录
测试因等待元素而超时
  • 确保开发服务器正在运行,且已加载扩展
  • 检查浏览器控制台中是否有扩展加载错误
  • 初次等待元素出现时使用更长的超时时间(15000ms+)
MSW 处理程序未拦截请求
  • 检查控制台中是否有显示处理程序注册情况的 [MSW] 日志
  • 确认处理程序 URL 模式与实际 API 调用相匹配
  • 使用浏览器 DevTools 的 Network 标签页查看实际请求 URL

可运行示例

请参阅 umbraco-backoffice-skills/examples/tree-example/Client/ 中的 tree-example:

路径说明
src/index.ts根据条件加载清单的入口点
src/msw/handlers.ts用于自定义 API 的 MSW 处理程序
tests/mock-repo/模拟仓库测试
tests/msw/MSW 测试
cd tree-example/Client
export UMBRACO_CLIENT_PATH=/path/to/Umbraco-CMS/src/Umbraco.Web.UI.Client

npm run test:msw        # Run MSW tests
npm run test:mock-repo  # Run mock repository tests

模拟了哪些内容?

MSW 为所有后台 API 提供模拟数据:

  • 文档、媒体、成员
  • 文档类型、媒体类型、成员类型
  • 数据类型、模板、样式表
  • 用户、用户组、权限
  • 语言、区域设置、词典项

评论

支持 Markdown