mcpField
U
umbraco-mocked-backoffice

umbraco-mocked-backoffice

github.com/umbracoTesting & quality27374 installsUpdated 6w ago
Repository

About umbraco-mocked-backoffice

A skill for testing Umbraco backoffice extensions in the backoffice UI with API calls mocked, without requiring a .NET backend. It documents MSW and mock repository approaches and Playwright-based test setup; external extension use is awaiting an Umbraco update.

Key features

  1. 01Run backoffice UI without a .NET backend
  2. 02Mock APIs with MSW handlers
  3. 03Use mock repositories for predictable data
  4. 04Set up Playwright tests for extensions
  5. 05Document mock data for backoffice APIs

Use cases

  1. 01Visually test extensions during development
  2. 02Exercise loading and error states with mocked requests
  3. 03Run extension UI tests without backend setup
  4. 04Demonstrate an extension without infrastructure

Install

Copy the command and run it in a terminal.

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

After installing, type/umbraco-mocked-backofficein Claude Code to confirm it loaded.

Bundled filespatterns/external-msw-handlers.mdpatterns/mock-repository-pattern.md

Details

From the project README

Umbraco Mocked Backoffice

Status: This skill is currently awaiting an update from Umbraco to allow external extensions to use the mocked backoffice. The patterns documented here work when running from within the Umbraco-CMS source repository.

Run the full Umbraco backoffice UI with all API calls mocked - no .NET backend required.

When to Use

  • Visually test extensions during development
  • Rapid iteration without backend deployment
  • Test extensions in realistic UI environment
  • Demonstrate extensions without infrastructure
  • CI/CD testing without backend setup

Related Skills

  • umbraco-example-generator - Set up extensions for mocked backoffice (start here)
  • umbraco-testing - Master skill for testing overview
  • umbraco-unit-testing - Test extension logic in isolation
  • umbraco-e2e-testing - Test against a real Umbraco instance

Two Mocking Approaches

Extensions with custom APIs can use two mocking approaches:

ApproachUse CaseBest For
MSW HandlersNetwork-level API mockingTesting error handling, loading states, retries
Mock RepositoryApplication-level mockingTesting UI with predictable data (recommended)

Both approaches require MSW to be enabled (VITE_UMBRACO_USE_MSW=on) for core Umbraco APIs.


Setup

Create Your Extension

Use the umbraco-example-generator skill to set up your extension:

Invoke: skill: umbraco-example-generator

This covers:

  • Cloning Umbraco-CMS repository
  • Extension structure and src/index.ts requirements
  • Running with VITE_EXAMPLE_PATH and npm run dev
Add Testing Dependencies
{
  "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
Directory Structure
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

Entry Point (src/index.ts)

The entry point conditionally loads MSW handlers or mock manifests based on environment:

// 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;

Running Tests

Self-contained external-example support (automatic)

Loading an extension from a path outside the Umbraco client requires the client to support an absolute VITE_EXAMPLE_PATH (via Vite's /@fs/) plus a shared-package resolver so @umbraco-cms/backoffice, lit and @umbraco-ui/uui resolve to the client's single copy. Stock released v18 only supports relative (./examples/...) paths.

To stay self-contained, this repo's mocked configs wire a Playwright globalSetup/globalTeardown (plugins/umbraco-testing-skills/harness/mocked-backoffice/) that injects that support into the client at UMBRACO_CLIENT_PATH before the dev server starts and reverts it afterwards (file-backup based; it detects clients that already have the support and no-ops). So no CMS branch is required — only a built v18 client (npm ci in Umbraco.Web.UI.Client).

First run against a freshly-patched client may re-optimize deps and briefly restart the dev server (a flaky first navigation); the configs set retries to absorb it. Warm runs are stable.

Environment Variables
VariableValuePurpose
VITE_EXAMPLE_PATH/path/to/extension/ClientPath to extension directory
VITE_UMBRACO_USE_MSWonEnable MSW for core Umbraco APIs
VITE_USE_MOCK_REPOonUse mock repository instead of MSW handlers
UMBRACO_CLIENT_PATH/path/to/Umbraco.Web.UI.ClientPath to Umbraco client (for Playwright)
Manual Dev Server
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
Run Tests
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 Config Example

Create 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'] },
    },
  ],
});

For mock-repo tests, change the command to include 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}`,

Test Patterns

Navigation Helper
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 });
}
Testing Tree Items
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 Mock Document URLs
Document NameURL Path
The Simplest Document/section/content/workspace/document/edit/the-simplest-document-id
All properties/section/content/workspace/document/edit/all-property-editors-document-id

Troubleshooting

Extension not appearing
  • Check that your extension exports a manifests array from src/index.ts
  • Check browser console for errors
  • Verify VITE_EXAMPLE_PATH points to the Client directory
Tests timeout waiting for elements
  • Ensure the dev server is running with your extension loaded
  • Check the browser console for extension loading errors
  • Use longer timeouts (15000ms+) for initial element appearance
MSW handlers not intercepting requests
  • Check console for [MSW] logs showing handler registration
  • Verify handler URL patterns match the actual API calls
  • Use browser DevTools Network tab to see actual request URLs

Working Example

See tree-example in umbraco-backoffice-skills/examples/tree-example/Client/:

PathDescription
src/index.tsEntry point with conditional manifest loading
src/msw/handlers.tsMSW handlers for custom API
tests/mock-repo/Mock repository tests
tests/msw/MSW tests
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

What's Mocked?

MSW provides mock data for all backoffice APIs:

  • Documents, media, members
  • Document types, media types, member types
  • Data types, templates, stylesheets
  • Users, user groups, permissions
  • Languages, cultures, dictionary items

Comments

Markdown supported